From e5f7c55c5495369c091ab5aec449638d7abd5a50 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Mon, 6 Jul 2015 12:34:35 -0700 Subject: Documentation in Packet, more work on path push, and clean up ancient legacy support code in Switch. --- node/IncomingPacket.cpp | 4 +- node/Packet.cpp | 2 +- node/Packet.hpp | 132 +++++++++++++++++++++++++++++++++++++----------- node/Switch.cpp | 16 ++++-- 4 files changed, 116 insertions(+), 38 deletions(-) (limited to 'node') diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index 7e2bcdaa..f45a1279 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -595,9 +595,7 @@ bool IncomingPacket::_doEXT_FRAME(const RuntimeEnvironment *RR,const SharedPtr

address().toString().c_str(),_remoteAddress.toString().c_str(),to.toString().c_str(),network->id()); return true; } - } - - if (to != network->mac()) { + } else if (to != network->mac()) { if (!network->permitsBridging(RR->identity.address())) { TRACE("dropped EXT_FRAME from %s@%s(%s) to %s: I cannot bridge to %.16llx or bridging disabled on network",from.toString().c_str(),peer->address().toString().c_str(),_remoteAddress.toString().c_str(),to.toString().c_str(),network->id()); return true; diff --git a/node/Packet.cpp b/node/Packet.cpp index 4d58815e..2c73a087 100644 --- a/node/Packet.cpp +++ b/node/Packet.cpp @@ -51,7 +51,7 @@ const char *Packet::verbString(Verb v) case VERB_MULTICAST_GATHER: return "MULTICAST_GATHER"; case VERB_MULTICAST_FRAME: return "MULTICAST_FRAME"; case VERB_SET_EPHEMERAL_KEY: return "SET_EPHEMERAL_KEY"; - case VERB_PHYSICAL_ADDRESS_PUSH: return "PHYSICAL_ADDRESS_PUSH"; + case VERB_PUSH_DIRECT_PATHS: return "PUSH_DIRECT_PATHS"; } return "(unknown)"; } diff --git a/node/Packet.hpp b/node/Packet.hpp index d9f8d888..eeeef720 100644 --- a/node/Packet.hpp +++ b/node/Packet.hpp @@ -71,13 +71,14 @@ /** * Maximum hop count allowed by packet structure (3 bits, 0-7) * - * This is not necessarily the maximum hop counter after which - * relaying is no longer performed. + * This is a protocol constant. It's the maximum allowed by the length + * of the hop counter -- three bits. See node/Constants.hpp for the + * pragmatic forwarding limit, which is typically lower. */ #define ZT_PROTO_MAX_HOPS 7 /** - * Cipher suite: Curve25519/Poly1305/Salsa20/12 without payload encryption + * Cipher suite: Curve25519/Poly1305/Salsa20/12/NOCRYPT * * This specifies Poly1305 MAC using a 32-bit key derived from the first * 32 bytes of a Salsa20/12 keystream as in the Salsa20/12 cipher suite, @@ -103,9 +104,7 @@ * * This message is encrypted with the latest negotiated ephemeral (PFS) * key pair and cipher suite. If authentication fails, VERB_SET_EPHEMERAL_KEY - * may be sent to renegotiate ephemeral keys. To prevent attacks, this - * attempted renegotiation should be limited to some sane rate such as - * once per second. + * may be sent to renegotiate ephemeral keys. */ #define ZT_PROTO_CIPHER_SUITE__EPHEMERAL 7 @@ -113,7 +112,7 @@ * DEPRECATED payload encrypted flag, will be removed for re-use soon. * * This has been replaced by the two-bit cipher suite selection field where - * a value of 0 indicated unencrypted (but authenticated) messages. + * a value of 0 indicates unencrypted (but authenticated) messages. */ #define ZT_PROTO_FLAG_ENCRYPTED 0x80 @@ -132,11 +131,68 @@ /** * Rounds used for Salsa20 encryption in ZT + * + * Discussion: + * + * DJB (Salsa20's designer) designed Salsa20 with a significant margin of 20 + * rounds, but has said repeatedly that 12 is likely sufficient. So far (as of + * July 2015) there are no published attacks against 12 rounds, let alone 20. + * + * In cryptography, a "break" means something different from what it means in + * common discussion. If a cipher is 256 bits strong and someone finds a way + * to reduce key search to 254 bits, this constitues a "break" in the academic + * literature. 254 bits is still far beyond what can be leveraged to accomplish + * a "break" as most people would understand it -- the actual decryption and + * reading of traffic. + * + * Nevertheless, "attacks only get better" as cryptographers like to say. As + * a result, they recommend not using anything that's shown any weakness even + * if that weakness is so far only meaningful to academics. It may be a sign + * of a deeper problem. + * + * So why choose a lower round count? + * + * Turns out the speed difference is nontrivial. On a Macbook Pro (Core i3) 20 + * rounds of SSE-optimized Salsa20 achieves ~508mb/sec/core, while 12 rounds + * hits ~832mb/sec/core. ZeroTier is designed for multiple objectives: + * security, simplicity, and performance. In this case a deference was made + * for performance. + * + * Meta discussion: + * + * The cipher is not the thing you should be paranoid about. + * + * I'll qualify that. If the cipher is known to be weak, like RC4, or has a + * key size that is too small, like DES, then yes you should worry about + * the cipher. + * + * But if the cipher is strong and your adversary is anyone other than the + * intelligence apparatus of a major superpower, you are fine in that + * department. + * + * Go ahead. Search for the last ten vulnerabilities discovered in SSL. Not + * a single one involved the breaking of a cipher. Now broaden your search. + * Look for issues with SSH, IPSec, etc. The only cipher-related issues you + * will find might involve the use of RC4 or MD5, algorithms with known + * issues or small key/digest sizes. But even weak ciphers are difficult to + * exploit in the real world -- you usually need a lot of data and a lot of + * compute time. No, virtually EVERY security vulnerability you will find + * involves a problem with the IMPLEMENTATION not with the cipher. + * + * A flaw in ZeroTier's protocol or code is incredibly, unbelievably + * more likely than a flaw in Salsa20 or any other cipher or cryptographic + * primitive it uses. We're talking odds of dying in a car wreck vs. odds of + * being personally impacted on the head by a meteorite. Nobody without a + * billion dollar budget is going to break into your network by actually + * cracking Salsa20/12 (or even /8) in the field. + * + * So stop worrying about the cipher unless you are, say, the Kremlin and your + * adversary is the NSA and the GCHQ. In that case... well that's above my + * pay grade. I'll just say defense in depth. */ #define ZT_PROTO_SALSA20_ROUNDS 12 -// Indices of fields in normal packet header -- do not change as this -// might require both code rework and will break compatibility. +// Field indexes in packet header #define ZT_PACKET_IDX_IV 0 #define ZT_PACKET_IDX_DEST 8 #define ZT_PACKET_IDX_SOURCE 13 @@ -147,16 +203,19 @@ /** * Packet buffer size (can be changed) + * + * The current value is big enough for ZT_MAX_PACKET_FRAGMENTS, the pragmatic + * packet fragment limit, times the default UDP MTU. Most packets won't be + * this big. */ #define ZT_PROTO_MAX_PACKET_LENGTH (ZT_MAX_PACKET_FRAGMENTS * ZT_UDP_DEFAULT_PAYLOAD_MTU) /** - * Minimum viable packet length (also length of header) + * Minimum viable packet length (a.k.a. header length) */ #define ZT_PROTO_MIN_PACKET_LENGTH ZT_PACKET_IDX_PAYLOAD -// Indexes of fields in fragment header -- also can't be changed without -// breaking compatibility. +// Indexes of fields in fragment header #define ZT_PACKET_FRAGMENT_IDX_PACKET_ID 0 #define ZT_PACKET_FRAGMENT_IDX_DEST 8 #define ZT_PACKET_FRAGMENT_IDX_FRAGMENT_INDICATOR 13 @@ -165,7 +224,7 @@ #define ZT_PACKET_FRAGMENT_IDX_PAYLOAD 16 /** - * Value found at ZT_PACKET_FRAGMENT_IDX_FRAGMENT_INDICATOR in fragments + * Magic number found at ZT_PACKET_FRAGMENT_IDX_FRAGMENT_INDICATOR */ #define ZT_PACKET_FRAGMENT_INDICATOR ZT_ADDRESS_RESERVED_PREFIX @@ -175,23 +234,26 @@ #define ZT_PROTO_MIN_FRAGMENT_LENGTH ZT_PACKET_FRAGMENT_IDX_PAYLOAD /** - * Length of LAN beacon packets + * DEPRECATED: length of LAN beacon packets */ #define ZT_PROTO_BEACON_LENGTH 13 /** - * Index of address in a LAN beacon + * DEPRECATED: index of address in a LAN beacon */ #define ZT_PROTO_BEACON_IDX_ADDRESS 8 -// Destination address types from HELLO and OK(HELLO) +// Destination address types from HELLO, OK(HELLO), and other message types #define ZT_PROTO_DEST_ADDRESS_TYPE_NONE 0 -#define ZT_PROTO_DEST_ADDRESS_TYPE_ETHERNET 1 +#define ZT_PROTO_DEST_ADDRESS_TYPE_ZEROTIER 1 // reserved but unused +#define ZT_PROTO_DEST_ADDRESS_TYPE_ETHERNET 2 // future use +#define ZT_PROTO_DEST_ADDRESS_TYPE_BLUETOOTH 3 // future use #define ZT_PROTO_DEST_ADDRESS_TYPE_IPV4 4 +#define ZT_PROTO_DEST_ADDRESS_TYPE_LTE_DIRECT 5 // future use #define ZT_PROTO_DEST_ADDRESS_TYPE_IPV6 6 // Ephemeral key record flags -#define ZT_PROTO_EPHEMERAL_KEY_FLAG_FIPS 0x01 +#define ZT_PROTO_EPHEMERAL_KEY_FLAG_FIPS 0x01 // future use // Ephemeral key record symmetric cipher types #define ZT_PROTO_EPHEMERAL_KEY_SYMMETRIC_CIPHER_SALSA2012_POLY1305 0x01 @@ -680,10 +742,8 @@ public: /* Network configuration refresh request: * <[...] array of 64-bit network IDs> * - * This message can be sent by the network configuration master node - * to request that nodes refresh their network configuration. It can - * thus be used to "push" updates so that network config changes will - * take effect quickly. + * This can be sent by the network controller to inform a node that it + * should now make a NETWORK_CONFIG_REQUEST. * * It does not generate an OK or ERROR message, and is treated only as * a hint to refresh now. @@ -769,7 +829,7 @@ public: */ VERB_MULTICAST_FRAME = 14, - /* Ephemeral (PFS) key push: + /* Ephemeral (PFS) key push: (UNFINISHED, NOT IMPLEMENTED YET) * <[2] flags (unused and reserved, must be 0)> * <[2] length of padding / extra field section> * <[...] padding / extra field section> @@ -826,21 +886,35 @@ public: VERB_SET_EPHEMERAL_KEY = 15, /* Push of potential endpoints for direct communication: + * <[1] flags (unused, must be zero)> + * <[2] 16-bit number of paths> + * <[...] paths> + * + * Path record format: * <[1] flags> - * <[2] number of addresses> - * <[...] address types and addresses> + * <[1] metric from 0 (highest priority) to 255 (lowest priority)> + * <[1] address type> + * <[...] address> + * + * Path record flags: + * 0x01 - Blacklist this path, do not use + * 0x02 - Reliable path (no keepalives, etc. necessary) + * 0x04 - Trusted path (encryption and authentication optional) + * + * None of the above flags are implemented yet as of 1.0.4. They're + * reserved for future use. * * Address types and addresses are of the same format as the destination * address type and address in HELLO. * * The receiver may, upon receiving a push, attempt to establish a - * direct link to one or more of the indicated addresses. Senders should - * only send address pushes to peers that they have some relationship - * with such as a shared network membership or a mutual trust. + * direct link to one or more of the indicated addresses. It is the + * responsibility of the sender to limit which peers it pushes direct + * paths to to those with whom it has a trust relationship. * * OK/ERROR are not generated. */ - VERB_PHYSICAL_ADDRESS_PUSH = 16 + VERB_PUSH_DIRECT_PATHS = 16 }; /** diff --git a/node/Switch.cpp b/node/Switch.cpp index 236c1e66..18e17a60 100644 --- a/node/Switch.cpp +++ b/node/Switch.cpp @@ -167,6 +167,8 @@ void Switch::onLocalEthernet(const SharedPtr &network,const MAC &from,c Address toZT(to.toAddress(network->id())); if (network->isAllowed(toZT)) { + const bool includeCom = network->peerNeedsOurMembershipCertificate(toZT,RR->node->now()); + /* if (network->peerNeedsOurMembershipCertificate(toZT,RR->node->now())) { // TODO: once there are no more <1.0.0 nodes around, we can // bundle this with EXT_FRAME instead of sending two packets. @@ -174,12 +176,17 @@ void Switch::onLocalEthernet(const SharedPtr &network,const MAC &from,c nconf->com().serialize(outp); send(outp,true,network->id()); } + */ - if (fromBridged) { - // EXT_FRAME is used for bridging or if we want to include a COM + if ((true)||(fromBridged)||(includeCom)) { Packet outp(toZT,RR->identity.address(),Packet::VERB_EXT_FRAME); outp.append(network->id()); - outp.append((unsigned char)0); + if (includeCom) { + outp.append((unsigned char)0x01); // 0x01 -- COM included + nconf->com().serialize(outp); + } else { + outp.append((unsigned char)0x00); + } to.appendTo(outp); from.appendTo(outp); outp.append((uint16_t)etherType); @@ -187,7 +194,6 @@ void Switch::onLocalEthernet(const SharedPtr &network,const MAC &from,c outp.compress(); send(outp,true,network->id()); } else { - // FRAME is a shorter version that can be used when there's no bridging and no COM Packet outp(toZT,RR->identity.address(),Packet::VERB_FRAME); outp.append(network->id()); outp.append((uint16_t)etherType); @@ -196,7 +202,7 @@ void Switch::onLocalEthernet(const SharedPtr &network,const MAC &from,c send(outp,true,network->id()); } - //TRACE("%.16llx: UNICAST: %s -> %s etherType==%s(%.4x) vlanId==%u len==%u fromBridged==%d",network->id(),from.toString().c_str(),to.toString().c_str(),etherTypeName(etherType),etherType,vlanId,len,(int)fromBridged); + //TRACE("%.16llx: UNICAST: %s -> %s etherType==%s(%.4x) vlanId==%u len==%u fromBridged==%d includeCom==%d",network->id(),from.toString().c_str(),to.toString().c_str(),etherTypeName(etherType),etherType,vlanId,len,(int)fromBridged,(int)includeCom); } else { TRACE("%.16llx: UNICAST: %s -> %s etherType==%s dropped, destination not a member of private network",network->id(),from.toString().c_str(),to.toString().c_str(),etherTypeName(etherType)); } -- cgit v1.2.3 From 9743db3538d1f1bc94f8ac4b1e75e19227e20efe Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Mon, 6 Jul 2015 12:37:37 -0700 Subject: docs --- node/Packet.hpp | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'node') diff --git a/node/Packet.hpp b/node/Packet.hpp index eeeef720..5572e235 100644 --- a/node/Packet.hpp +++ b/node/Packet.hpp @@ -700,6 +700,12 @@ public: * * Certificate contains network ID, peer it was issued for, etc. * + * Note that in the current code base, separate COM pushes are not done. + * Instead the "bundled COM" options are utilized in EXT_FRAME and + * MULTICAST_FRAME to push the COM along with frames. This is slightly + * more efficient. But we'll keep this simple message around in case we + * want to use it in the future. + * * OK/ERROR are not generated. */ VERB_NETWORK_MEMBERSHIP_CERTIFICATE = 10, -- cgit v1.2.3 From 35b5dcf89df77e372c8c06e62b30debd55426f98 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Mon, 6 Jul 2015 12:39:20 -0700 Subject: Kill debug line. --- node/Switch.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'node') diff --git a/node/Switch.cpp b/node/Switch.cpp index 18e17a60..f91bf5c9 100644 --- a/node/Switch.cpp +++ b/node/Switch.cpp @@ -178,7 +178,7 @@ void Switch::onLocalEthernet(const SharedPtr &network,const MAC &from,c } */ - if ((true)||(fromBridged)||(includeCom)) { + if ((fromBridged)||(includeCom)) { Packet outp(toZT,RR->identity.address(),Packet::VERB_EXT_FRAME); outp.append(network->id()); if (includeCom) { -- cgit v1.2.3 From 6bfbc43e3c1de27648c974fc783573487391e0a2 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Mon, 6 Jul 2015 12:46:27 -0700 Subject: Include COM with EXT_FRAME in bridged case. --- node/Switch.cpp | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) (limited to 'node') diff --git a/node/Switch.cpp b/node/Switch.cpp index f91bf5c9..f022122e 100644 --- a/node/Switch.cpp +++ b/node/Switch.cpp @@ -167,7 +167,6 @@ void Switch::onLocalEthernet(const SharedPtr &network,const MAC &from,c Address toZT(to.toAddress(network->id())); if (network->isAllowed(toZT)) { - const bool includeCom = network->peerNeedsOurMembershipCertificate(toZT,RR->node->now()); /* if (network->peerNeedsOurMembershipCertificate(toZT,RR->node->now())) { // TODO: once there are no more <1.0.0 nodes around, we can @@ -178,6 +177,7 @@ void Switch::onLocalEthernet(const SharedPtr &network,const MAC &from,c } */ + const bool includeCom = network->peerNeedsOurMembershipCertificate(toZT,RR->node->now()); if ((fromBridged)||(includeCom)) { Packet outp(toZT,RR->identity.address(),Packet::VERB_EXT_FRAME); outp.append(network->id()); @@ -216,16 +216,14 @@ void Switch::onLocalEthernet(const SharedPtr &network,const MAC &from,c Address bridges[ZT_MAX_BRIDGE_SPAM]; unsigned int numBridges = 0; + /* Create an array of up to ZT_MAX_BRIDGE_SPAM recipients for this bridged frame. */ bridges[0] = network->findBridgeTo(to); if ((bridges[0])&&(bridges[0] != RR->identity.address())&&(network->isAllowed(bridges[0]))&&(network->permitsBridging(bridges[0]))) { - // We have a known bridge route for this MAC. + /* We have a known bridge route for this MAC, send it there. */ ++numBridges; } else if (!nconf->activeBridges().empty()) { /* If there is no known route, spam to up to ZT_MAX_BRIDGE_SPAM active - * bridges. This is similar to what many switches do -- if they do not - * know which port corresponds to a MAC, they send it to all ports. If - * there aren't any active bridges, numBridges will stay 0 and packet - * is dropped. */ + * bridges. If someone responds, we'll learn the route. */ std::vector

::const_iterator ab(nconf->activeBridges().begin()); if (nconf->activeBridges().size() <= ZT_MAX_BRIDGE_SPAM) { // If there are <= ZT_MAX_BRIDGE_SPAM active bridges, spam them all @@ -251,7 +249,12 @@ void Switch::onLocalEthernet(const SharedPtr &network,const MAC &from,c for(unsigned int b=0;bidentity.address(),Packet::VERB_EXT_FRAME); outp.append(network->id()); - outp.append((unsigned char)0); + if (network->peerNeedsOurMembershipCertificate(bridges[b],RR->node->now())) { + outp.append((unsigned char)0x01); // 0x01 -- COM included + nconf->com().serialize(outp); + } else { + outp.append((unsigned char)0); + } to.appendTo(outp); from.appendTo(outp); outp.append((uint16_t)etherType); -- cgit v1.2.3 From feddd946f9d3ac6a643968582610eb4ffaebb69d Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Mon, 6 Jul 2015 13:51:25 -0700 Subject: For curiosity add Salsa20/8 to benchmarks. --- node/Path.hpp | 185 ---------------------------------------------------- node/RemotePath.hpp | 185 ++++++++++++++++++++++++++++++++++++++++++++++++++++ selftest.cpp | 18 +++++ 3 files changed, 203 insertions(+), 185 deletions(-) delete mode 100644 node/Path.hpp create mode 100644 node/RemotePath.hpp (limited to 'node') diff --git a/node/Path.hpp b/node/Path.hpp deleted file mode 100644 index 393b7225..00000000 --- a/node/Path.hpp +++ /dev/null @@ -1,185 +0,0 @@ -/* - * ZeroTier One - Network Virtualization Everywhere - * Copyright (C) 2011-2015 ZeroTier, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * -- - * - * ZeroTier may be used and distributed under the terms of the GPLv3, which - * are available at: http://www.gnu.org/licenses/gpl-3.0.html - * - * If you would like to embed ZeroTier into a commercial application or - * redistribute it in a modified binary form, please contact ZeroTier Networks - * LLC. Start here: http://www.zerotier.com/ - */ - -#ifndef ZT_PATH_HPP -#define ZT_PATH_HPP - -#include -#include - -#include -#include -#include - -#include "Constants.hpp" -#include "Node.hpp" -#include "InetAddress.hpp" -#include "Utils.hpp" -#include "AntiRecursion.hpp" -#include "RuntimeEnvironment.hpp" - -namespace ZeroTier { - -/** - * WAN address and protocol for reaching a peer - * - * This structure is volatile and memcpy-able, and depends on - * InetAddress being similarly safe. - */ -class Path -{ -public: - Path() : - _addr(), - _lastSend(0), - _lastReceived(0), - _fixed(false) {} - - Path(const Path &p) throw() { memcpy(this,&p,sizeof(Path)); } - - Path(const InetAddress &addr,bool fixed) : - _addr(addr), - _lastSend(0), - _lastReceived(0), - _fixed(fixed) {} - - inline void init(const InetAddress &addr,bool fixed) - { - _addr = addr; - _lastSend = 0; - _lastReceived = 0; - _fixed = fixed; - } - - inline Path &operator=(const Path &p) - { - if (this != &p) - memcpy(this,&p,sizeof(Path)); - return *this; - } - - inline const InetAddress &address() const throw() { return _addr; } - - inline uint64_t lastSend() const throw() { return _lastSend; } - inline uint64_t lastReceived() const throw() { return _lastReceived; } - - /** - * Called when a packet is sent to this path - * - * This is called automatically by Path::send(). - * - * @param t Time of send - */ - inline void sent(uint64_t t) - throw() - { - _lastSend = t; - } - - /** - * Called when a packet is received from this path - * - * @param t Time of receive - */ - inline void received(uint64_t t) - throw() - { - _lastReceived = t; - } - - /** - * @return Is this a fixed path? - */ - inline bool fixed() const throw() { return _fixed; } - - /** - * @param f New value of fixed path flag - */ - inline void setFixed(bool f) throw() { _fixed = f; } - - /** - * @param now Current time - * @return True if this path is fixed or has received data in last ACTIVITY_TIMEOUT ms - */ - inline bool active(uint64_t now) const - throw() - { - return ( (_fixed) || ((now - _lastReceived) < ZT_PEER_ACTIVITY_TIMEOUT) ); - } - - /** - * Send a packet via this path - * - * @param RR Runtime environment - * @param data Packet data - * @param len Packet length - * @param now Current time - * @return True if transport reported success - */ - inline bool send(const RuntimeEnvironment *RR,const void *data,unsigned int len,uint64_t now) - { - if (RR->node->putPacket(_addr,data,len)) { - sent(now); - RR->antiRec->logOutgoingZT(data,len); - return true; - } - return false; - } - - /** - * @param now Current time - * @return Human-readable address and other information about this path - */ - inline std::string toString(uint64_t now) const - { - char tmp[1024]; - Utils::snprintf(tmp,sizeof(tmp),"%s(%s)", - _addr.toString().c_str(), - ((_fixed) ? "fixed" : (active(now) ? "active" : "inactive")) - ); - return std::string(tmp); - } - - inline operator bool() const throw() { return (_addr); } - - inline bool operator==(const Path &p) const throw() { return (_addr == p._addr); } - inline bool operator!=(const Path &p) const throw() { return (_addr != p._addr); } - inline bool operator<(const Path &p) const throw() { return (_addr < p._addr); } - inline bool operator>(const Path &p) const throw() { return (_addr > p._addr); } - inline bool operator<=(const Path &p) const throw() { return (_addr <= p._addr); } - inline bool operator>=(const Path &p) const throw() { return (_addr >= p._addr); } - -private: - InetAddress _addr; - uint64_t _lastSend; - uint64_t _lastReceived; - bool _fixed; -}; - -} // namespace ZeroTier - -#endif diff --git a/node/RemotePath.hpp b/node/RemotePath.hpp new file mode 100644 index 00000000..393b7225 --- /dev/null +++ b/node/RemotePath.hpp @@ -0,0 +1,185 @@ +/* + * ZeroTier One - Network Virtualization Everywhere + * Copyright (C) 2011-2015 ZeroTier, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * -- + * + * ZeroTier may be used and distributed under the terms of the GPLv3, which + * are available at: http://www.gnu.org/licenses/gpl-3.0.html + * + * If you would like to embed ZeroTier into a commercial application or + * redistribute it in a modified binary form, please contact ZeroTier Networks + * LLC. Start here: http://www.zerotier.com/ + */ + +#ifndef ZT_PATH_HPP +#define ZT_PATH_HPP + +#include +#include + +#include +#include +#include + +#include "Constants.hpp" +#include "Node.hpp" +#include "InetAddress.hpp" +#include "Utils.hpp" +#include "AntiRecursion.hpp" +#include "RuntimeEnvironment.hpp" + +namespace ZeroTier { + +/** + * WAN address and protocol for reaching a peer + * + * This structure is volatile and memcpy-able, and depends on + * InetAddress being similarly safe. + */ +class Path +{ +public: + Path() : + _addr(), + _lastSend(0), + _lastReceived(0), + _fixed(false) {} + + Path(const Path &p) throw() { memcpy(this,&p,sizeof(Path)); } + + Path(const InetAddress &addr,bool fixed) : + _addr(addr), + _lastSend(0), + _lastReceived(0), + _fixed(fixed) {} + + inline void init(const InetAddress &addr,bool fixed) + { + _addr = addr; + _lastSend = 0; + _lastReceived = 0; + _fixed = fixed; + } + + inline Path &operator=(const Path &p) + { + if (this != &p) + memcpy(this,&p,sizeof(Path)); + return *this; + } + + inline const InetAddress &address() const throw() { return _addr; } + + inline uint64_t lastSend() const throw() { return _lastSend; } + inline uint64_t lastReceived() const throw() { return _lastReceived; } + + /** + * Called when a packet is sent to this path + * + * This is called automatically by Path::send(). + * + * @param t Time of send + */ + inline void sent(uint64_t t) + throw() + { + _lastSend = t; + } + + /** + * Called when a packet is received from this path + * + * @param t Time of receive + */ + inline void received(uint64_t t) + throw() + { + _lastReceived = t; + } + + /** + * @return Is this a fixed path? + */ + inline bool fixed() const throw() { return _fixed; } + + /** + * @param f New value of fixed path flag + */ + inline void setFixed(bool f) throw() { _fixed = f; } + + /** + * @param now Current time + * @return True if this path is fixed or has received data in last ACTIVITY_TIMEOUT ms + */ + inline bool active(uint64_t now) const + throw() + { + return ( (_fixed) || ((now - _lastReceived) < ZT_PEER_ACTIVITY_TIMEOUT) ); + } + + /** + * Send a packet via this path + * + * @param RR Runtime environment + * @param data Packet data + * @param len Packet length + * @param now Current time + * @return True if transport reported success + */ + inline bool send(const RuntimeEnvironment *RR,const void *data,unsigned int len,uint64_t now) + { + if (RR->node->putPacket(_addr,data,len)) { + sent(now); + RR->antiRec->logOutgoingZT(data,len); + return true; + } + return false; + } + + /** + * @param now Current time + * @return Human-readable address and other information about this path + */ + inline std::string toString(uint64_t now) const + { + char tmp[1024]; + Utils::snprintf(tmp,sizeof(tmp),"%s(%s)", + _addr.toString().c_str(), + ((_fixed) ? "fixed" : (active(now) ? "active" : "inactive")) + ); + return std::string(tmp); + } + + inline operator bool() const throw() { return (_addr); } + + inline bool operator==(const Path &p) const throw() { return (_addr == p._addr); } + inline bool operator!=(const Path &p) const throw() { return (_addr != p._addr); } + inline bool operator<(const Path &p) const throw() { return (_addr < p._addr); } + inline bool operator>(const Path &p) const throw() { return (_addr > p._addr); } + inline bool operator<=(const Path &p) const throw() { return (_addr <= p._addr); } + inline bool operator>=(const Path &p) const throw() { return (_addr >= p._addr); } + +private: + InetAddress _addr; + uint64_t _lastSend; + uint64_t _lastReceived; + bool _fixed; +}; + +} // namespace ZeroTier + +#endif diff --git a/selftest.cpp b/selftest.cpp index 27c81c25..f84abcc0 100644 --- a/selftest.cpp +++ b/selftest.cpp @@ -193,6 +193,24 @@ static int testCrypto() std::cout << "[crypto] Salsa20 SSE: DISABLED" << std::endl; #endif + std::cout << "[crypto] Benchmarking Salsa20/8... "; std::cout.flush(); + { + unsigned char *bb = (unsigned char *)::malloc(1234567); + for(unsigned int i=0;i<1234567;++i) + bb[i] = (unsigned char)i; + Salsa20 s20(s20TV0Key,256,s20TV0Iv,8); + double bytes = 0.0; + uint64_t start = OSUtils::now(); + for(unsigned int i=0;i<200;++i) { + s20.encrypt(bb,bb,1234567); + bytes += 1234567.0; + } + uint64_t end = OSUtils::now(); + SHA512::hash(buf1,bb,1234567); + std::cout << ((bytes / 1048576.0) / ((double)(end - start) / 1000.0)) << " MiB/second (" << Utils::hex(buf1,16) << ')' << std::endl; + ::free((void *)bb); + } + std::cout << "[crypto] Benchmarking Salsa20/12... "; std::cout.flush(); { unsigned char *bb = (unsigned char *)::malloc(1234567); -- cgit v1.2.3 From 93bb934d4e77fd1436ebe81336d396fa769aa59b Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Mon, 6 Jul 2015 14:08:13 -0700 Subject: Some cleanup, docs, and Path -> Path > RemotePath refactor. --- node/Node.cpp | 6 +-- node/Packet.hpp | 32 ++++++++++---- node/Path.hpp | 113 +++++++++++++++++++++++++++++++++++++++++++++++++ node/Peer.cpp | 15 ++++--- node/Peer.hpp | 36 +++++++++------- node/RemotePath.hpp | 97 ++++++++++-------------------------------- node/SelfAwareness.cpp | 2 +- node/Switch.cpp | 2 +- node/Topology.cpp | 2 +- 9 files changed, 197 insertions(+), 108 deletions(-) create mode 100644 node/Path.hpp (limited to 'node') diff --git a/node/Node.cpp b/node/Node.cpp index 8cdc6d62..8eacf753 100644 --- a/node/Node.cpp +++ b/node/Node.cpp @@ -388,10 +388,10 @@ ZT1_PeerList *Node::peers() const p->latency = pi->second->latency(); p->role = RR->topology->isRoot(pi->second->identity()) ? ZT1_PEER_ROLE_ROOT : ZT1_PEER_ROLE_LEAF; - std::vector paths(pi->second->paths()); - Path *bestPath = pi->second->getBestPath(_now); + std::vector paths(pi->second->paths()); + RemotePath *bestPath = pi->second->getBestPath(_now); p->pathCount = 0; - for(std::vector::iterator path(paths.begin());path!=paths.end();++path) { + for(std::vector::iterator path(paths.begin());path!=paths.end();++path) { memcpy(&(p->paths[p->pathCount].address),&(path->address()),sizeof(struct sockaddr_storage)); p->paths[p->pathCount].lastSend = path->lastSend(); p->paths[p->pathCount].lastReceive = path->lastReceived(); diff --git a/node/Packet.hpp b/node/Packet.hpp index 5572e235..14fac1bf 100644 --- a/node/Packet.hpp +++ b/node/Packet.hpp @@ -899,16 +899,17 @@ public: * Path record format: * <[1] flags> * <[1] metric from 0 (highest priority) to 255 (lowest priority)> + * <[2] length of extended path characteristics or 0 for none> + * <[...] extended path characteristics> * <[1] address type> * <[...] address> * * Path record flags: - * 0x01 - Blacklist this path, do not use - * 0x02 - Reliable path (no keepalives, etc. necessary) - * 0x04 - Trusted path (encryption and authentication optional) - * - * None of the above flags are implemented yet as of 1.0.4. They're - * reserved for future use. + * 0x01 - Forget this path if it is currently known + * 0x02 - Blacklist this path, do not use + * 0x04 - Reliable path (no NAT keepalives, etc. are necessary) + * 0x08 - Disable encryption (trust: privacy) + * 0x10 - Disable encryption and authentication (trust: ultimate) * * Address types and addresses are of the same format as the destination * address type and address in HELLO. @@ -916,9 +917,24 @@ public: * The receiver may, upon receiving a push, attempt to establish a * direct link to one or more of the indicated addresses. It is the * responsibility of the sender to limit which peers it pushes direct - * paths to to those with whom it has a trust relationship. + * paths to to those with whom it has a trust relationship. The receiver + * must obey any restrictions provided such as exclusivity or blacklists. + * OK responses to this message are optional. * - * OK/ERROR are not generated. + * Note that a direct path push does not imply that learned paths can't + * be used unless they are blacklisted explicitly or unless flag 0x01 + * is set. + * + * Only a subset of this functionality is currently implemented: basic + * path pushing and learning. Metrics, most flags, and OK responses are + * not yet implemented as of 1.0.4. + * + * OK response payload: + * <[2] 16-bit number of active direct paths we already have> + * <[2] 16-bit number of paths in push that we don't already have> + * <[2] 16-bit number of new paths we are trying (or will try)> + * + * ERROR is presently not sent. */ VERB_PUSH_DIRECT_PATHS = 16 }; diff --git a/node/Path.hpp b/node/Path.hpp new file mode 100644 index 00000000..b43b3f6d --- /dev/null +++ b/node/Path.hpp @@ -0,0 +1,113 @@ +/* + * ZeroTier One - Network Virtualization Everywhere + * Copyright (C) 2011-2015 ZeroTier, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * -- + * + * ZeroTier may be used and distributed under the terms of the GPLv3, which + * are available at: http://www.gnu.org/licenses/gpl-3.0.html + * + * If you would like to embed ZeroTier into a commercial application or + * redistribute it in a modified binary form, please contact ZeroTier Networks + * LLC. Start here: http://www.zerotier.com/ + */ + +#ifndef ZT_PATH_HPP +#define ZT_PATH_HPP + +#include "Constants.hpp" +#include "InetAddress.hpp" +#include "Utils.hpp" + +namespace ZeroTier { + +class Path +{ +public: + enum Trust + { + TRUST_NORMAL, + TRUST_PRIVACY, + TRUST_ULTIMATE + }; + + Path() : + _addr(), + _metric(0), + _trust(TRUST_NORMAL), + _reliable(false), + _fixed(false) + { + } + + Path(const InetAddress &addr,int metric,Trust trust,bool reliable,bool fixed) : + _addr(addr), + _metric(metric), + _trust(trust), + _reliable(reliable), + _fixed(fixed) + { + } + + /** + * @return Physical address + */ + inline const InetAddress &address() const throw() { return _addr; } + + /** + * @return Metric (higher == worse) or negative if path is blacklisted + */ + inline int metric() const throw() { return _metric; } + + /** + * @return Path trust level + */ + inline Trust trust() const throw() { return _trust; } + + /** + * @return True if path is considered reliable (no NAT keepalives etc. are needed) + */ + inline bool reliable() const throw() { return _reliable; } + + /** + * @return Is this a fixed path? + */ + inline bool fixed() const throw() { return _fixed; } + + /** + * @return True if address is non-NULL + */ + inline operator bool() const throw() { return (_addr); } + + // Comparisons are by address only + inline bool operator==(const Path &p) const throw() { return (_addr == p._addr); } + inline bool operator!=(const Path &p) const throw() { return (_addr != p._addr); } + inline bool operator<(const Path &p) const throw() { return (_addr < p._addr); } + inline bool operator>(const Path &p) const throw() { return (_addr > p._addr); } + inline bool operator<=(const Path &p) const throw() { return (_addr <= p._addr); } + inline bool operator>=(const Path &p) const throw() { return (_addr >= p._addr); } + +protected: + InetAddress _addr; + int _metric; // negative == blacklisted + Trust _trust; + bool _reliable; + bool _fixed; +}; + +} // namespace ZeroTier + +#endif diff --git a/node/Peer.cpp b/node/Peer.cpp index 96caa72c..cb046b9b 100644 --- a/node/Peer.cpp +++ b/node/Peer.cpp @@ -46,6 +46,7 @@ Peer::Peer(const Identity &myIdentity,const Identity &peerIdentity) _lastMulticastFrame(0), _lastAnnouncedTo(0), _lastPathConfirmationSent(0), + _lastDirectPathPush(0), _vMajor(0), _vMinor(0), _vRevision(0), @@ -86,7 +87,7 @@ void Peer::received( if (!pathIsConfirmed) { if ((verb == Packet::VERB_OK)&&(inReVerb == Packet::VERB_HELLO)) { // Learn paths if they've been confirmed via a HELLO - Path *slot = (Path *)0; + RemotePath *slot = (RemotePath *)0; if (np < ZT1_MAX_PEER_NETWORK_PATHS) { // Add new path slot = &(_paths[np++]); @@ -101,7 +102,7 @@ void Peer::received( } } if (slot) { - slot->init(remoteAddr,false); + *slot = RemotePath(remoteAddr,false); slot->received(now); _numPaths = np; pathIsConfirmed = true; @@ -193,7 +194,7 @@ void Peer::attemptToContactAt(const RuntimeEnvironment *RR,const InetAddress &at void Peer::doPingAndKeepalive(const RuntimeEnvironment *RR,uint64_t now) { - Path *const bestPath = getBestPath(now); + RemotePath *const bestPath = getBestPath(now); if ((bestPath)&&(bestPath->active(now))) { if ((now - bestPath->lastReceived()) >= ZT_PEER_DIRECT_PING_DELAY) { TRACE("PING %s(%s)",_id.address().toString().c_str(),bestPath->address().toString().c_str()); @@ -207,7 +208,11 @@ void Peer::doPingAndKeepalive(const RuntimeEnvironment *RR,uint64_t now) } } -void Peer::addPath(const Path &newp) +//void Peer::pushDirectPaths(const std::vector &dps,uint64_t now,bool force) +//{ +//} + +void Peer::addPath(const RemotePath &newp) { unsigned int np = _numPaths; @@ -218,7 +223,7 @@ void Peer::addPath(const Path &newp) } } - Path *slot = (Path *)0; + RemotePath *slot = (RemotePath *)0; if (np < ZT1_MAX_PEER_NETWORK_PATHS) { // Add new path slot = &(_paths[np++]); diff --git a/node/Peer.hpp b/node/Peer.hpp index 8d8b7cb4..6ca29393 100644 --- a/node/Peer.hpp +++ b/node/Peer.hpp @@ -40,7 +40,7 @@ #include "../include/ZeroTierOne.h" #include "RuntimeEnvironment.hpp" -#include "Path.hpp" +#include "RemotePath.hpp" #include "Address.hpp" #include "Utils.hpp" #include "Identity.hpp" @@ -53,11 +53,7 @@ namespace ZeroTier { /** - * Peer on P2P Network - * - * This struture is not locked, volatile, and memcpy-able. NonCopyable - * semantics are just there to prevent bugs, not because it isn't safe - * to copy. + * Peer on P2P Network (virtual layer 1) */ class Peer : NonCopyable { @@ -130,9 +126,9 @@ public: * @param now Current time * @return Best path or NULL if there are no active (or fixed) direct paths */ - inline Path *getBestPath(uint64_t now) + inline RemotePath *getBestPath(uint64_t now) { - Path *bestPath = (Path *)0; + RemotePath *bestPath = (RemotePath *)0; uint64_t lrMax = 0; for(unsigned int p=0,np=_numPaths;p= lrMax)) { @@ -152,14 +148,14 @@ public: * @param now Current time * @return Path used on success or NULL on failure */ - inline Path *send(const RuntimeEnvironment *RR,const void *data,unsigned int len,uint64_t now) + inline RemotePath *send(const RuntimeEnvironment *RR,const void *data,unsigned int len,uint64_t now) { - Path *bestPath = getBestPath(now); + RemotePath *bestPath = getBestPath(now); if (bestPath) { if (bestPath->send(RR,data,len,now)) return bestPath; } - return (Path *)0; + return (RemotePath *)0; } /** @@ -182,12 +178,21 @@ public: */ void doPingAndKeepalive(const RuntimeEnvironment *RR,uint64_t now); + /** + * Push direct paths (if within rate limit) + * + * @param dps Direct paths to me to push to this peer + * @param now Current time + * @param force If true, force regardless of when we pushed direct paths last + */ + void pushDirectPaths(const std::vector &dps,uint64_t now,bool force); + /** * @return All known direct paths to this peer */ - inline std::vector paths() const + inline std::vector paths() const { - std::vector pp; + std::vector pp; for(unsigned int p=0,np=_numPaths;p #include #include -#include #include -#include "Constants.hpp" +#include "Path.hpp" #include "Node.hpp" -#include "InetAddress.hpp" -#include "Utils.hpp" #include "AntiRecursion.hpp" #include "RuntimeEnvironment.hpp" namespace ZeroTier { /** - * WAN address and protocol for reaching a peer + * Path to a remote peer * - * This structure is volatile and memcpy-able, and depends on - * InetAddress being similarly safe. + * This extends Path to include status information about path activity. */ -class Path +class RemotePath : public Path { public: - Path() : - _addr(), + RemotePath() : + Path(), _lastSend(0), - _lastReceived(0), - _fixed(false) {} + _lastReceived(0) {} - Path(const Path &p) throw() { memcpy(this,&p,sizeof(Path)); } - - Path(const InetAddress &addr,bool fixed) : - _addr(addr), + RemotePath(const InetAddress &addr,bool fixed) : + Path(addr,0,TRUST_NORMAL,false,fixed), _lastSend(0), - _lastReceived(0), - _fixed(fixed) {} + _lastReceived(0) {} - inline void init(const InetAddress &addr,bool fixed) - { - _addr = addr; - _lastSend = 0; - _lastReceived = 0; - _fixed = fixed; - } + inline uint64_t lastSend() const throw() { return _lastSend; } + inline uint64_t lastReceived() const throw() { return _lastReceived; } - inline Path &operator=(const Path &p) + /** + * @param f New value of parent 'fixed' field + */ + inline void setFixed(const bool f) + throw() { - if (this != &p) - memcpy(this,&p,sizeof(Path)); - return *this; + _fixed = f; } - inline const InetAddress &address() const throw() { return _addr; } - - inline uint64_t lastSend() const throw() { return _lastSend; } - inline uint64_t lastReceived() const throw() { return _lastReceived; } - /** - * Called when a packet is sent to this path + * Called when a packet is sent to this remote path * - * This is called automatically by Path::send(). + * This is called automatically by RemotePath::send(). * * @param t Time of send */ @@ -101,7 +85,7 @@ public: } /** - * Called when a packet is received from this path + * Called when a packet is received from this remote path * * @param t Time of receive */ @@ -111,16 +95,6 @@ public: _lastReceived = t; } - /** - * @return Is this a fixed path? - */ - inline bool fixed() const throw() { return _fixed; } - - /** - * @param f New value of fixed path flag - */ - inline void setFixed(bool f) throw() { _fixed = f; } - /** * @param now Current time * @return True if this path is fixed or has received data in last ACTIVITY_TIMEOUT ms @@ -150,34 +124,9 @@ public: return false; } - /** - * @param now Current time - * @return Human-readable address and other information about this path - */ - inline std::string toString(uint64_t now) const - { - char tmp[1024]; - Utils::snprintf(tmp,sizeof(tmp),"%s(%s)", - _addr.toString().c_str(), - ((_fixed) ? "fixed" : (active(now) ? "active" : "inactive")) - ); - return std::string(tmp); - } - - inline operator bool() const throw() { return (_addr); } - - inline bool operator==(const Path &p) const throw() { return (_addr == p._addr); } - inline bool operator!=(const Path &p) const throw() { return (_addr != p._addr); } - inline bool operator<(const Path &p) const throw() { return (_addr < p._addr); } - inline bool operator>(const Path &p) const throw() { return (_addr > p._addr); } - inline bool operator<=(const Path &p) const throw() { return (_addr <= p._addr); } - inline bool operator>=(const Path &p) const throw() { return (_addr >= p._addr); } - private: - InetAddress _addr; uint64_t _lastSend; uint64_t _lastReceived; - bool _fixed; }; } // namespace ZeroTier diff --git a/node/SelfAwareness.cpp b/node/SelfAwareness.cpp index 9f7c41d7..00015788 100644 --- a/node/SelfAwareness.cpp +++ b/node/SelfAwareness.cpp @@ -120,7 +120,7 @@ void SelfAwareness::iam(const Address &reporter,const InetAddress &reporterPhysi // they are still considered alive so that we will re-establish direct links. SharedPtr sn(RR->topology->getBestRoot()); if (sn) { - Path *snp = sn->getBestPath(now); + RemotePath *snp = sn->getBestPath(now); if (snp) { for(std::vector< SharedPtr >::const_iterator p(rset.peersReset.begin());p!=rset.peersReset.end();++p) { if ((*p)->alive(now)) { diff --git a/node/Switch.cpp b/node/Switch.cpp index f022122e..891c4a45 100644 --- a/node/Switch.cpp +++ b/node/Switch.cpp @@ -733,7 +733,7 @@ bool Switch::_trySend(const Packet &packet,bool encrypt,uint64_t nwid) if (peer) { const uint64_t now = RR->node->now(); - Path *viaPath = peer->getBestPath(now); + RemotePath *viaPath = peer->getBestPath(now); if (!viaPath) { SharedPtr relay; diff --git a/node/Topology.cpp b/node/Topology.cpp index 2b1cc31f..b255080e 100644 --- a/node/Topology.cpp +++ b/node/Topology.cpp @@ -62,7 +62,7 @@ void Topology::setRootServers(const std::map< Identity,std::vector if (!p) p = SharedPtr(new Peer(RR->identity,i->first)); for(std::vector::const_iterator j(i->second.begin());j!=i->second.end();++j) - p->addPath(Path(*j,true)); + p->addPath(RemotePath(*j,true)); p->use(now); _rootPeers.push_back(p); } -- cgit v1.2.3 From 255320e2a6f340fca6c53f3a0516f99a4cb4562b Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Mon, 6 Jul 2015 14:39:28 -0700 Subject: pushDirectPaths() implementation --- node/Constants.hpp | 5 +++++ node/Packet.hpp | 1 - node/Peer.cpp | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++--- node/Peer.hpp | 3 ++- 4 files changed, 67 insertions(+), 5 deletions(-) (limited to 'node') diff --git a/node/Constants.hpp b/node/Constants.hpp index ac9dbc99..1f0d8426 100644 --- a/node/Constants.hpp +++ b/node/Constants.hpp @@ -319,6 +319,11 @@ */ #define ZT_MIN_PATH_CONFIRMATION_INTERVAL 5000 +/** + * Interval between direct path pushes in milliseconds + */ +#define ZT_DIRECT_PATH_PUSH_INTERVAL 300000 + /** * Sanity limit on maximum bridge routes * diff --git a/node/Packet.hpp b/node/Packet.hpp index 14fac1bf..9b86709a 100644 --- a/node/Packet.hpp +++ b/node/Packet.hpp @@ -892,7 +892,6 @@ public: VERB_SET_EPHEMERAL_KEY = 15, /* Push of potential endpoints for direct communication: - * <[1] flags (unused, must be zero)> * <[2] 16-bit number of paths> * <[...] paths> * diff --git a/node/Peer.cpp b/node/Peer.cpp index cb046b9b..ec04357a 100644 --- a/node/Peer.cpp +++ b/node/Peer.cpp @@ -208,9 +208,66 @@ void Peer::doPingAndKeepalive(const RuntimeEnvironment *RR,uint64_t now) } } -//void Peer::pushDirectPaths(const std::vector &dps,uint64_t now,bool force) -//{ -//} +void Peer::pushDirectPaths(const RuntimeEnvironment *RR,const std::vector &dps,uint64_t now,bool force) +{ + if (((now - _lastDirectPathPush) >= ZT_DIRECT_PATH_PUSH_INTERVAL)||(force)) { + _lastDirectPathPush = now; + + std::vector::const_iterator p(dps.begin()); + while (p != dps.end()) { + Packet outp(_id.address(),RR->identity.address(),Packet::VERB_PUSH_DIRECT_PATHS); + outp.addSize(2); // leave room for count + + unsigned int count = 0; + while ((p != dps.end())&&((outp.size() + 24) < ZT_PROTO_MAX_PACKET_LENGTH)) { + uint8_t addressType = 4; + switch(p->address().ss_family) { + case AF_INET: + break; + case AF_INET6: + addressType = 6; + break; + default: + ++p; + continue; + } + + uint8_t flags = 0; + if (p->metric() < 0) + flags |= (0x01 | 0x02); // forget and blacklist + else { + if (p->reliable()) + flags |= 0x04; // no NAT keepalives and such + switch(p->trust()) { + default: + break; + case Path::TRUST_PRIVACY: + flags |= 0x08; // no encryption + break; + case Path::TRUST_ULTIMATE: + flags |= (0x08 | 0x10); // no encryption, no authentication (redundant but go ahead and set both) + break; + } + } + + outp.append(flags); + outp.append((uint8_t)((p->metric() >= 0) ? ((p->metric() <= 255) ? p->metric() : 255) : 0)); + outp.append((uint16_t)0); + outp.append(addressType); + outp.append(p->address().rawIpData(),((addressType == 4) ? 4 : 16)); + outp.append((uint16_t)p->address().port()); + + ++count; + ++p; + } + + if (count) { + outp.setAt(ZT_PACKET_IDX_PAYLOAD,(uint16_t)count); + RR->sw->send(outp,true,0); + } + } + } +} void Peer::addPath(const RemotePath &newp) { diff --git a/node/Peer.hpp b/node/Peer.hpp index 6ca29393..2af3b9e6 100644 --- a/node/Peer.hpp +++ b/node/Peer.hpp @@ -181,11 +181,12 @@ public: /** * Push direct paths (if within rate limit) * + * @param RR Runtime environment * @param dps Direct paths to me to push to this peer * @param now Current time * @param force If true, force regardless of when we pushed direct paths last */ - void pushDirectPaths(const std::vector &dps,uint64_t now,bool force); + void pushDirectPaths(const RuntimeEnvironment *RR,const std::vector &dps,uint64_t now,bool force); /** * @return All known direct paths to this peer -- cgit v1.2.3 From 1632aec102f6cd03006bfa1c296f266928a0d9c4 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Mon, 6 Jul 2015 14:53:27 -0700 Subject: Check Network::isAllowed() always on multicast send. --- node/OutboundMulticast.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'node') diff --git a/node/OutboundMulticast.cpp b/node/OutboundMulticast.cpp index f62046be..bda7ab00 100644 --- a/node/OutboundMulticast.cpp +++ b/node/OutboundMulticast.cpp @@ -102,8 +102,14 @@ void OutboundMulticast::init( void OutboundMulticast::sendOnly(const RuntimeEnvironment *RR,const Address &toAddr) { + SharedPtr network(RR->node->network(_nwid)); + + if (!network) + return; + if (!network->isAllowed(toAddr)) + return; + if (_haveCom) { - SharedPtr network(RR->node->network(_nwid)); if (network->peerNeedsOurMembershipCertificate(toAddr,RR->node->now())) { _packetWithCom.newInitializationVector(); _packetWithCom.setDestination(toAddr); @@ -112,6 +118,7 @@ void OutboundMulticast::sendOnly(const RuntimeEnvironment *RR,const Address &toA return; } } + //TRACE(">>MC %.16llx -> %s (without COM)",(unsigned long long)this,toAddr.toString().c_str()); _packetNoCom.newInitializationVector(); _packetNoCom.setDestination(toAddr); -- cgit v1.2.3 From fad9dff2db26662e1496329057884b3b928cb1c9 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Mon, 6 Jul 2015 15:05:04 -0700 Subject: Almost all of GitHub issue #180 --- node/IncomingPacket.cpp | 6 ++++++ node/IncomingPacket.hpp | 1 + node/Node.hpp | 10 ++++++++++ node/Switch.cpp | 10 +++++++++- 4 files changed, 26 insertions(+), 1 deletion(-) (limited to 'node') diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index f45a1279..7634f54f 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -84,6 +84,7 @@ bool IncomingPacket::tryDecode(const RuntimeEnvironment *RR) case Packet::VERB_NETWORK_CONFIG_REFRESH: return _doNETWORK_CONFIG_REFRESH(RR,peer); 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); } } else { RR->sw->requestWhois(source()); @@ -882,6 +883,11 @@ bool IncomingPacket::_doMULTICAST_FRAME(const RuntimeEnvironment *RR,const Share return true; } +bool IncomingPacket::_doPUSH_DIRECT_PATHS(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 174fa38d..3bf7737d 100644 --- a/node/IncomingPacket.hpp +++ b/node/IncomingPacket.hpp @@ -121,6 +121,7 @@ private: bool _doNETWORK_CONFIG_REFRESH(const RuntimeEnvironment *RR,const SharedPtr &peer); 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); // Send an ERROR_NEED_MEMBERSHIP_CERTIFICATE to a peer indicating that an updated cert is needed to join void _sendErrorNeedCertificate(const RuntimeEnvironment *RR,const SharedPtr &peer,uint64_t nwid); diff --git a/node/Node.hpp b/node/Node.hpp index 2d2898b5..5c7cfae2 100644 --- a/node/Node.hpp +++ b/node/Node.hpp @@ -43,6 +43,7 @@ #include "Mutex.hpp" #include "MAC.hpp" #include "Network.hpp" +#include "Path.hpp" #undef TRACE #ifdef ZT_TRACE @@ -171,6 +172,12 @@ public: return nw; } + inline std::vector directPaths() const + { + Mutex::Lock _l(_directPaths_m); + return _directPaths; + } + inline bool dataStorePut(const char *name,const void *data,unsigned int len,bool secure) { return (_dataStorePutFunction(reinterpret_cast(this),_uPtr,name,data,len,(int)secure) == 0); } inline bool dataStorePut(const char *name,const std::string &data,bool secure) { return dataStorePut(name,(const void *)data.data(),(unsigned int)data.length(),secure); } inline void dataStoreDelete(const char *name) { _dataStorePutFunction(reinterpret_cast(this),_uPtr,name,(const void *)0,0,0); } @@ -236,6 +243,9 @@ private: std::vector< std::pair< uint64_t, SharedPtr > > _networks; Mutex _networks_m; + std::vector _directPaths; + Mutex _directPaths_m; + Mutex _backgroundTasksLock; uint64_t _now; diff --git a/node/Switch.cpp b/node/Switch.cpp index 891c4a45..7600624f 100644 --- a/node/Switch.cpp +++ b/node/Switch.cpp @@ -733,10 +733,16 @@ bool Switch::_trySend(const Packet &packet,bool encrypt,uint64_t nwid) if (peer) { const uint64_t now = RR->node->now(); + if (nwid) { + // If this packet has an associated network, give the peer additional hints for direct connectivity + peer->pushDirectPaths(RR,RR->node->directPaths(),now,false); + } + RemotePath *viaPath = peer->getBestPath(now); if (!viaPath) { SharedPtr relay; + // See if this network has a preferred relay (if packet has an associated network) if (nwid) { SharedPtr network(RR->node->network(nwid)); if (network) { @@ -754,6 +760,7 @@ bool Switch::_trySend(const Packet &packet,bool encrypt,uint64_t nwid) } } + // Otherwise relay off a root server if (!relay) relay = RR->topology->getBestRoot(); @@ -770,7 +777,7 @@ bool Switch::_trySend(const Packet &packet,bool encrypt,uint64_t nwid) if (viaPath->send(RR,tmp.data(),chunkSize,now)) { if (chunkSize < tmp.size()) { - // Too big for one bite, fragment the rest + // Too big for one packet, fragment the rest unsigned int fragStart = chunkSize; unsigned int remaining = tmp.size() - chunkSize; unsigned int fragsRemaining = (remaining / (ZT_UDP_DEFAULT_PAYLOAD_MTU - ZT_PROTO_MIN_FRAGMENT_LENGTH)); @@ -786,6 +793,7 @@ bool Switch::_trySend(const Packet &packet,bool encrypt,uint64_t nwid) remaining -= chunkSize; } } + return true; } } else { -- cgit v1.2.3 From 79e9a8bcc26491a43577082ec7edb86020f7ec00 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Mon, 6 Jul 2015 15:28:48 -0700 Subject: Almost everything for GitHub issue #180 except direct path map setup. --- node/Constants.hpp | 10 ---------- node/IncomingPacket.cpp | 30 ++++++++++++++++++++++++++++++ node/Node.cpp | 16 +--------------- node/Node.hpp | 1 - node/Packet.hpp | 21 +-------------------- node/Peer.cpp | 6 +++++- node/Switch.cpp | 24 ++---------------------- node/Switch.hpp | 2 -- 8 files changed, 39 insertions(+), 71 deletions(-) (limited to 'node') diff --git a/node/Constants.hpp b/node/Constants.hpp index 1f0d8426..d15fef13 100644 --- a/node/Constants.hpp +++ b/node/Constants.hpp @@ -304,16 +304,6 @@ */ #define ZT_ANTIRECURSION_HISTORY_SIZE 16 -/** - * How often to send LAN beacons - */ -#define ZT_BEACON_INTERVAL 30000 - -/** - * Do not respond to any beacon more often than this - */ -#define ZT_MIN_BEACON_RESPONSE_INTERVAL 2500 - /** * Minimum delay between attempts to confirm new paths to peers (to avoid HELLO flooding) */ diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index 7634f54f..87669ba7 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -885,6 +885,36 @@ bool IncomingPacket::_doMULTICAST_FRAME(const RuntimeEnvironment *RR,const Share bool IncomingPacket::_doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,const SharedPtr &peer) { + try { + unsigned int count = at(ZT_PACKET_IDX_PAYLOAD); + unsigned int ptr = ZT_PACKET_IDX_PAYLOAD + 2; + + while (count) { // if ptr overflows Buffer will throw + unsigned int flags = (*this)[ptr++]; + /*int metric = (*this)[ptr++];*/ ++ptr; + unsigned int extLen = at(ptr); ptr += 2; + ptr += extLen; // unused right now + unsigned int addrType = (*this)[ptr++]; + unsigned int addrLen = (*this)[ptr++]; + switch(addrType) { + case 4: { + InetAddress a(field(ptr,4),4,at(ptr + 4)); + if ((flags & (0x01 | 0x02)) == 0) + peer->attemptToContactAt(RR,a,RR->node->now()); + } break; + case 6: { + InetAddress a(field(ptr,16),16,at(ptr + 16)); + if ((flags & (0x01 | 0x02)) == 0) + peer->attemptToContactAt(RR,a,RR->node->now()); + } break; + } + ptr += addrLen; + } + } catch (std::exception &exc) { + TRACE("dropped PUSH_DIRECT_PATHS from %s(%s): unexpected exception: %s",source().toString().c_str(),_remoteAddress.toString().c_str(),exc.what()); + } catch ( ... ) { + TRACE("dropped PUSH_DIRECT_PATHS from %s(%s): unexpected exception: (unknown)",source().toString().c_str(),_remoteAddress.toString().c_str()); + } return true; } diff --git a/node/Node.cpp b/node/Node.cpp index 8eacf753..7c70db97 100644 --- a/node/Node.cpp +++ b/node/Node.cpp @@ -78,8 +78,7 @@ Node::Node( _networks_m(), _now(now), _lastPingCheck(0), - _lastHousekeepingRun(0), - _lastBeacon(0) + _lastHousekeepingRun(0) { _newestVersionSeen[0] = ZEROTIER_ONE_VERSION_MAJOR; _newestVersionSeen[1] = ZEROTIER_ONE_VERSION_MINOR; @@ -269,19 +268,6 @@ ZT1_ResultCode Node::processBackgroundTasks(uint64_t now,volatile uint64_t *next _online = ((now - pfunc.lastReceiveFromUpstream) < ZT_PEER_ACTIVITY_TIMEOUT); if (oldOnline != _online) postEvent(_online ? ZT1_EVENT_ONLINE : ZT1_EVENT_OFFLINE); - - // Send LAN beacons - if ((now - _lastBeacon) >= ZT_BEACON_INTERVAL) { - _lastBeacon = now; - char beacon[13]; - void *p = beacon; - *(reinterpret_cast(p)) = RR->prng->next32(); - p = beacon + 4; - *(reinterpret_cast(p)) = RR->prng->next32(); - RR->identity.address().copyTo(beacon + 8,5); - RR->antiRec->logOutgoingZT(beacon,13); - putPacket(ZT_DEFAULTS.v4Broadcast,beacon,13); - } } catch ( ... ) { return ZT1_RESULT_FATAL_ERROR_INTERNAL; } diff --git a/node/Node.hpp b/node/Node.hpp index 5c7cfae2..cd8d68fc 100644 --- a/node/Node.hpp +++ b/node/Node.hpp @@ -251,7 +251,6 @@ private: uint64_t _now; uint64_t _lastPingCheck; uint64_t _lastHousekeepingRun; - uint64_t _lastBeacon; unsigned int _newestVersionSeen[3]; // major, minor, revision bool _online; }; diff --git a/node/Packet.hpp b/node/Packet.hpp index 9b86709a..50471b06 100644 --- a/node/Packet.hpp +++ b/node/Packet.hpp @@ -233,16 +233,6 @@ */ #define ZT_PROTO_MIN_FRAGMENT_LENGTH ZT_PACKET_FRAGMENT_IDX_PAYLOAD -/** - * DEPRECATED: length of LAN beacon packets - */ -#define ZT_PROTO_BEACON_LENGTH 13 - -/** - * DEPRECATED: index of address in a LAN beacon - */ -#define ZT_PROTO_BEACON_IDX_ADDRESS 8 - // Destination address types from HELLO, OK(HELLO), and other message types #define ZT_PROTO_DEST_ADDRESS_TYPE_NONE 0 #define ZT_PROTO_DEST_ADDRESS_TYPE_ZEROTIER 1 // reserved but unused @@ -388,16 +378,6 @@ namespace ZeroTier { * * 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. - * - * Beacon format and beacon packets: - * <[8] 8 random bytes> - * <[5] sender ZT address> - * - * A beacon is a 13-byte packet containing only the address of the sender. - * Receiving peers may or may not respond to beacons with a HELLO or other - * message to initiate direct communication. - * - * Beacons may be used for direct LAN announcement or NAT traversal. */ class Packet : public Buffer { @@ -901,6 +881,7 @@ public: * <[2] length of extended path characteristics or 0 for none> * <[...] extended path characteristics> * <[1] address type> + * <[1] address length in bytes> * <[...] address> * * Path record flags: diff --git a/node/Peer.cpp b/node/Peer.cpp index ec04357a..225fbdef 100644 --- a/node/Peer.cpp +++ b/node/Peer.cpp @@ -210,9 +210,12 @@ void Peer::doPingAndKeepalive(const RuntimeEnvironment *RR,uint64_t now) void Peer::pushDirectPaths(const RuntimeEnvironment *RR,const std::vector &dps,uint64_t now,bool force) { - if (((now - _lastDirectPathPush) >= ZT_DIRECT_PATH_PUSH_INTERVAL)||(force)) { + if ((!dps.empty())&&(((now - _lastDirectPathPush) >= ZT_DIRECT_PATH_PUSH_INTERVAL)||(force))) { _lastDirectPathPush = now; + TRACE("pushing %u direct paths to %s",(unsigned int)dps.size(),_id.address().toString().c_str()); + printf("pushing %u direct paths to %s",(unsigned int)dps.size(),_id.address().toString().c_str()); + std::vector::const_iterator p(dps.begin()); while (p != dps.end()) { Packet outp(_id.address(),RR->identity.address(),Packet::VERB_PUSH_DIRECT_PATHS); @@ -254,6 +257,7 @@ void Peer::pushDirectPaths(const RuntimeEnvironment *RR,const std::vector outp.append((uint8_t)((p->metric() >= 0) ? ((p->metric() <= 255) ? p->metric() : 255) : 0)); outp.append((uint16_t)0); outp.append(addressType); + outp.append((addressType == 4) ? 6 : 18); outp.append(p->address().rawIpData(),((addressType == 4) ? 4 : 16)); outp.append((uint16_t)p->address().port()); diff --git a/node/Switch.cpp b/node/Switch.cpp index 7600624f..6a0b07cd 100644 --- a/node/Switch.cpp +++ b/node/Switch.cpp @@ -49,8 +49,7 @@ namespace ZeroTier { Switch::Switch(const RuntimeEnvironment *renv) : - RR(renv), - _lastBeacon(0) + RR(renv) { } @@ -61,9 +60,7 @@ Switch::~Switch() void Switch::onRemotePacket(const InetAddress &fromAddr,const void *data,unsigned int len) { try { - if (len == ZT_PROTO_BEACON_LENGTH) { - _handleBeacon(fromAddr,Buffer(data,len)); - } else if (len > ZT_PROTO_MIN_FRAGMENT_LENGTH) { + if (len > ZT_PROTO_MIN_FRAGMENT_LENGTH) { if (((const unsigned char *)data)[ZT_PACKET_FRAGMENT_IDX_FRAGMENT_INDICATOR] == ZT_PACKET_FRAGMENT_INDICATOR) { _handleRemotePacketFragment(fromAddr,data,len); } else if (len >= ZT_PROTO_MIN_PACKET_LENGTH) { @@ -696,23 +693,6 @@ void Switch::_handleRemotePacketHead(const InetAddress &fromAddr,const void *dat } } -void Switch::_handleBeacon(const InetAddress &fromAddr,const Buffer &data) -{ - Address beaconAddr(data.field(ZT_PROTO_BEACON_IDX_ADDRESS,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); - if (beaconAddr == RR->identity.address()) - return; - SharedPtr peer(RR->topology->getPeer(beaconAddr)); - if (peer) { - const uint64_t now = RR->node->now(); - if ((now - _lastBeacon) >= ZT_MIN_BEACON_RESPONSE_INTERVAL) { - _lastBeacon = now; - Packet outp(peer->address(),RR->identity.address(),Packet::VERB_NOP); - outp.armor(peer->key(),false); - RR->node->putPacket(fromAddr,outp.data(),outp.size()); - } - } -} - Address Switch::_sendWhoisRequest(const Address &addr,const Address *peersAlreadyConsulted,unsigned int numPeersAlreadyConsulted) { SharedPtr root(RR->topology->getBestRoot(peersAlreadyConsulted,numPeersAlreadyConsulted,false)); diff --git a/node/Switch.hpp b/node/Switch.hpp index 0ba4c138..af6e5938 100644 --- a/node/Switch.hpp +++ b/node/Switch.hpp @@ -183,12 +183,10 @@ public: private: void _handleRemotePacketFragment(const InetAddress &fromAddr,const void *data,unsigned int len); void _handleRemotePacketHead(const InetAddress &fromAddr,const void *data,unsigned int len); - void _handleBeacon(const InetAddress &fromAddr,const Buffer &data); Address _sendWhoisRequest(const Address &addr,const Address *peersAlreadyConsulted,unsigned int numPeersAlreadyConsulted); bool _trySend(const Packet &packet,bool encrypt,uint64_t nwid); const RuntimeEnvironment *const RR; - volatile uint64_t _lastBeacon; // Outsanding WHOIS requests and how many retries they've undergone struct WhoisRequest -- cgit v1.2.3 From 235f4762b700174c795b28de7d4fe2f70cddbcd8 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Mon, 6 Jul 2015 15:51:04 -0700 Subject: Plumbing for local interface addresses -- GitHub issue #180 --- include/ZeroTierOne.h | 32 ++++++++++++++++++++++++++++++++ node/Node.cpp | 28 ++++++++++++++++++++++++++++ node/Node.hpp | 5 +++++ node/Path.hpp | 21 +++++++-------------- node/RemotePath.hpp | 16 ++++++++++++---- 5 files changed, 84 insertions(+), 18 deletions(-) (limited to 'node') diff --git a/include/ZeroTierOne.h b/include/ZeroTierOne.h index b6ff69ab..4790795e 100644 --- a/include/ZeroTierOne.h +++ b/include/ZeroTierOne.h @@ -628,6 +628,15 @@ typedef struct unsigned long peerCount; } ZT1_PeerList; +/** + * Local interface trust levels + */ +typedef enum { + ZT1_LOCAL_INTERFACE_ADDRESS_TRUST_NORMAL = 0, + ZT1_LOCAL_INTERFACE_ADDRESS_TRUST_PRIVACY = 1, + ZT1_LOCAL_INTERFACE_ADDRESS_TRUST_ULTIMATE = 2 +} ZT1_LocalInterfaceAddressTrust; + /** * An instance of a ZeroTier One node (opaque) */ @@ -958,6 +967,29 @@ ZT1_VirtualNetworkList *ZT1_Node_networks(ZT1_Node *node); */ void ZT1_Node_freeQueryResult(ZT1_Node *node,void *qr); +/** + * Add a local interface address + * + * Local interface addresses may be added if you want remote peers + * with whom you have a trust relatinship (e.g. common network membership) + * to receive information about these endpoints as potential endpoints for + * direct communication. + * + * Take care that these are never ZeroTier interface addresses, otherwise + * strange things might happen or they simply won't work. + * + * @param addr Local interface address + * @param metric Local interface metric + * @param trust How much do you trust the local network under this interface? + * @param reliable If nonzero, this interface doesn't link to anything behind a NAT or stateful firewall + */ +void ZT1_Node_addLocalInterfaceAddress(ZT1_Node *node,const struct sockaddr_storage *addr,int metric,ZT1_LocalInterfaceAddressTrust trust,int reliable); + +/** + * Clear local interface addresses + */ +void ZT1_Node_clearLocalInterfaceAddresses(ZT1_Node *node); + /** * Set a network configuration master instance for this node * diff --git a/node/Node.cpp b/node/Node.cpp index 7c70db97..057dc285 100644 --- a/node/Node.cpp +++ b/node/Node.cpp @@ -426,6 +426,20 @@ void Node::freeQueryResult(void *qr) ::free(qr); } +void Node::addLocalInterfaceAddress(const struct sockaddr_storage *addr,int metric,ZT1_LocalInterfaceAddressTrust trust,int reliable) +{ + Mutex::Lock _l(_directPaths_m); + _directPaths.push_back(Path(*(reinterpret_cast(addr)),metric,(Path::Trust)trust,reliable != 0)); + std::sort(_directPaths.begin(),_directPaths.end()); + _directPaths.erase(std::unique(_directPaths.begin(),_directPaths.end()),_directPaths.end()); +} + +void Node::clearLocalInterfaceAddresses() +{ + Mutex::Lock _l(_directPaths_m); + _directPaths.clear(); +} + void Node::setNetconfMaster(void *networkControllerInstance) { RR->localNetworkController = reinterpret_cast(networkControllerInstance); @@ -679,6 +693,20 @@ void ZT1_Node_setNetconfMaster(ZT1_Node *node,void *networkControllerInstance) } catch ( ... ) {} } +void ZT1_Node_addLocalInterfaceAddress(ZT1_Node *node,const struct sockaddr_storage *addr,int metric,ZT1_LocalInterfaceAddressTrust trust,int reliable) +{ + try { + reinterpret_cast(node)->addLocalInterfaceAddress(addr,metric,trust,reliable); + } catch ( ... ) {} +} + +void ZT1_Node_clearLocalInterfaceAddresses(ZT1_Node *node) +{ + try { + reinterpret_cast(node)->clearLocalInterfaceAddresses(); + } catch ( ... ) {} +} + void ZT1_version(int *major,int *minor,int *revision,unsigned long *featureFlags) { if (major) *major = ZEROTIER_ONE_VERSION_MAJOR; diff --git a/node/Node.hpp b/node/Node.hpp index cd8d68fc..1c260545 100644 --- a/node/Node.hpp +++ b/node/Node.hpp @@ -104,6 +104,8 @@ public: ZT1_VirtualNetworkConfig *networkConfig(uint64_t nwid) const; ZT1_VirtualNetworkList *networks() const; void freeQueryResult(void *qr); + void addLocalInterfaceAddress(const struct sockaddr_storage *addr,int metric,ZT1_LocalInterfaceAddressTrust trust,int reliable); + void clearLocalInterfaceAddresses(); void setNetconfMaster(void *networkControllerInstance); // Internal functions ------------------------------------------------------ @@ -172,6 +174,9 @@ public: return nw; } + /** + * @return Potential direct paths to me a.k.a. local interface addresses + */ inline std::vector directPaths() const { Mutex::Lock _l(_directPaths_m); diff --git a/node/Path.hpp b/node/Path.hpp index b43b3f6d..80b9a3c0 100644 --- a/node/Path.hpp +++ b/node/Path.hpp @@ -37,28 +37,27 @@ namespace ZeroTier { class Path { public: + // Must be the same values as ZT1_LocalInterfaceAddressTrust in ZeroTierOne.h enum Trust { - TRUST_NORMAL, - TRUST_PRIVACY, - TRUST_ULTIMATE + TRUST_NORMAL = 0, + TRUST_PRIVACY = 1, + TRUST_ULTIMATE = 2 }; Path() : _addr(), _metric(0), _trust(TRUST_NORMAL), - _reliable(false), - _fixed(false) + _reliable(false) { } - Path(const InetAddress &addr,int metric,Trust trust,bool reliable,bool fixed) : + Path(const InetAddress &addr,int metric,Trust trust,bool reliable) : _addr(addr), _metric(metric), _trust(trust), - _reliable(reliable), - _fixed(fixed) + _reliable(reliable) { } @@ -82,11 +81,6 @@ public: */ inline bool reliable() const throw() { return _reliable; } - /** - * @return Is this a fixed path? - */ - inline bool fixed() const throw() { return _fixed; } - /** * @return True if address is non-NULL */ @@ -105,7 +99,6 @@ protected: int _metric; // negative == blacklisted Trust _trust; bool _reliable; - bool _fixed; }; } // namespace ZeroTier diff --git a/node/RemotePath.hpp b/node/RemotePath.hpp index 47c8916d..5592c8e1 100644 --- a/node/RemotePath.hpp +++ b/node/RemotePath.hpp @@ -52,18 +52,25 @@ public: RemotePath() : Path(), _lastSend(0), - _lastReceived(0) {} + _lastReceived(0), + _fixed(false) {} RemotePath(const InetAddress &addr,bool fixed) : - Path(addr,0,TRUST_NORMAL,false,fixed), + Path(addr,0,TRUST_NORMAL,false), _lastSend(0), - _lastReceived(0) {} + _lastReceived(0), + _fixed(fixed) {} inline uint64_t lastSend() const throw() { return _lastSend; } inline uint64_t lastReceived() const throw() { return _lastReceived; } /** - * @param f New value of parent 'fixed' field + * @return Is this a fixed path? + */ + inline bool fixed() const throw() { return _fixed; } + + /** + * @param f New value of fixed flag */ inline void setFixed(const bool f) throw() @@ -127,6 +134,7 @@ public: private: uint64_t _lastSend; uint64_t _lastReceived; + bool _fixed; }; } // namespace ZeroTier -- cgit v1.2.3 From a87cd2d0941d5cf6b88f630159946ae66d3e464e Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Mon, 6 Jul 2015 16:32:34 -0700 Subject: Unix side of local interface address awareness for GitHub issue #180. --- node/InetAddress.cpp | 8 +++++- node/Peer.cpp | 1 - service/OneService.cpp | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 76 insertions(+), 2 deletions(-) (limited to 'node') diff --git a/node/InetAddress.cpp b/node/InetAddress.cpp index 83de36d4..91bfbed6 100644 --- a/node/InetAddress.cpp +++ b/node/InetAddress.cpp @@ -91,7 +91,13 @@ InetAddress::IpScope InetAddress::ipScope() const const unsigned char *ip = reinterpret_cast(reinterpret_cast(this)->sin6_addr.s6_addr); if ((ip[0] & 0xf0) == 0xf0) { if (ip[0] == 0xff) return IP_SCOPE_MULTICAST; // ff00::/8 - if ((ip[0] == 0xfe)&&((ip[1] & 0xc0) == 0x80)) return IP_SCOPE_LINK_LOCAL; // fe80::/10 + if ((ip[0] == 0xfe)&&((ip[1] & 0xc0) == 0x80)) { + unsigned int k = 2; + while ((!ip[k])&&(k < 15)) ++k; + if ((k == 15)&&(ip[15] == 0x01)) + return IP_SCOPE_LOOPBACK; // fe80::1/128 + else return IP_SCOPE_LINK_LOCAL; // fe80::/10 + } if ((ip[0] & 0xfe) == 0xfc) return IP_SCOPE_PRIVATE; // fc00::/7 } unsigned int k = 0; diff --git a/node/Peer.cpp b/node/Peer.cpp index 225fbdef..f5fdf7dd 100644 --- a/node/Peer.cpp +++ b/node/Peer.cpp @@ -214,7 +214,6 @@ void Peer::pushDirectPaths(const RuntimeEnvironment *RR,const std::vector _lastDirectPathPush = now; TRACE("pushing %u direct paths to %s",(unsigned int)dps.size(),_id.address().toString().c_str()); - printf("pushing %u direct paths to %s",(unsigned int)dps.size(),_id.address().toString().c_str()); std::vector::const_iterator p(dps.begin()); while (p != dps.end()) { diff --git a/service/OneService.cpp b/service/OneService.cpp index 7532bf75..109050bf 100644 --- a/service/OneService.cpp +++ b/service/OneService.cpp @@ -78,8 +78,10 @@ class SqliteNetworkController; #include #else #include +#include #include #include +#include #endif // Include the right tap device driver for this platform -- add new platforms here @@ -123,6 +125,9 @@ namespace ZeroTier { typedef BSDEthernetTap EthernetTap; } // Attempt to engage TCP fallback after this many ms of no reply to packets sent to global-scope IPs #define ZT1_TCP_FALLBACK_AFTER 60000 +// How often to check for local interface addresses +#define ZT1_LOCAL_INTERFACE_CHECK_INTERVAL 300000 + namespace ZeroTier { namespace { @@ -405,6 +410,7 @@ public: _nextBackgroundTaskDeadline(0), _tcpFallbackTunnel((TcpConnection *)0), _termReason(ONE_STILL_RUNNING), + _port(port), _run(true) { struct sockaddr_in in4; @@ -501,6 +507,7 @@ public: _lastRestart = clockShouldBe; uint64_t lastTapMulticastGroupCheck = 0; uint64_t lastTcpFallbackResolve = 0; + uint64_t lastLocalInterfaceAddressCheck = 0; #ifdef ZT_AUTO_UPDATE uint64_t lastSoftwareUpdateCheck = 0; #endif // ZT_AUTO_UPDATE @@ -554,6 +561,66 @@ public: } } + if ((now - lastLocalInterfaceAddressCheck) >= ZT1_LOCAL_INTERFACE_CHECK_INTERVAL) { + lastLocalInterfaceAddressCheck = now; + +#ifdef __UNIX_LIKE__ + std::vector ztDevices; + { + Mutex::Lock _l(_taps_m); + for(std::map< uint64_t,EthernetTap *>::const_iterator t(_taps.begin());t!=_taps.end();++t) + ztDevices.push_back(t->second->deviceName()); + } + + struct ifaddrs *ifatbl = (struct ifaddrs *)0; + if ((getifaddrs(&ifatbl) == 0)&&(ifatbl)) { + _node->clearLocalInterfaceAddresses(); + struct ifaddrs *ifa = ifatbl; + while (ifa) { + if ((ifa->ifa_name)&&(ifa->ifa_addr)) { + bool isZT = false; + for(std::vector::const_iterator d(ztDevices.begin());d!=ztDevices.end();++d) { + if (*d == ifa->ifa_name) { + isZT = true; + break; + } + } + if (!isZT) { + InetAddress ip(ifa->ifa_addr); + if ((ip.ss_family == AF_INET)||(ip.ss_family == AF_INET6)) { + switch(ip.ipScope()) { + case InetAddress::IP_SCOPE_LINK_LOCAL: + case InetAddress::IP_SCOPE_PRIVATE: + case InetAddress::IP_SCOPE_PSEUDOPRIVATE: + case InetAddress::IP_SCOPE_SHARED: + case InetAddress::IP_SCOPE_GLOBAL: + ip.setPort(_port); + _node->addLocalInterfaceAddress(reinterpret_cast(&ip),0,ZT1_LOCAL_INTERFACE_ADDRESS_TRUST_NORMAL,0); + break; + default: + break; + } + } + } + } + ifa = ifa->ifa_next; + } + freeifaddrs(ifatbl); + } +#endif // __UNIX_LIKE__ + +#ifdef __WINDOWS__ + std::vector ztDevices; + { + Mutex::Lock _l(_taps_m); + for(std::map< uint64_t,EthernetTap *>::const_iterator t(_taps.begin());t!=_taps.end();++t) + ztDevices.push_back(t->second->deviceName()); + } + + // TODO +#endif // __WINDOWS__ + } + const unsigned long delay = (dl > now) ? (unsigned long)(dl - now) : 100; clockShouldBe = now + (uint64_t)delay; _phy.poll(delay); @@ -1158,6 +1225,8 @@ private: std::string _fatalErrorMessage; Mutex _termReason_m; + unsigned int _port; + bool _run; Mutex _run_m; }; -- cgit v1.2.3 From cac55105c3db6b0992b3ca6261b368bb14c7d144 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Mon, 6 Jul 2015 16:40:23 -0700 Subject: Fix a regression. --- node/OutboundMulticast.cpp | 2 -- node/Switch.cpp | 10 ---------- 2 files changed, 12 deletions(-) (limited to 'node') diff --git a/node/OutboundMulticast.cpp b/node/OutboundMulticast.cpp index bda7ab00..5809504a 100644 --- a/node/OutboundMulticast.cpp +++ b/node/OutboundMulticast.cpp @@ -106,8 +106,6 @@ void OutboundMulticast::sendOnly(const RuntimeEnvironment *RR,const Address &toA if (!network) return; - if (!network->isAllowed(toAddr)) - return; if (_haveCom) { if (network->peerNeedsOurMembershipCertificate(toAddr,RR->node->now())) { diff --git a/node/Switch.cpp b/node/Switch.cpp index 6a0b07cd..201f36d1 100644 --- a/node/Switch.cpp +++ b/node/Switch.cpp @@ -164,16 +164,6 @@ void Switch::onLocalEthernet(const SharedPtr &network,const MAC &from,c Address toZT(to.toAddress(network->id())); if (network->isAllowed(toZT)) { - /* - if (network->peerNeedsOurMembershipCertificate(toZT,RR->node->now())) { - // TODO: once there are no more <1.0.0 nodes around, we can - // bundle this with EXT_FRAME instead of sending two packets. - Packet outp(toZT,RR->identity.address(),Packet::VERB_NETWORK_MEMBERSHIP_CERTIFICATE); - nconf->com().serialize(outp); - send(outp,true,network->id()); - } - */ - const bool includeCom = network->peerNeedsOurMembershipCertificate(toZT,RR->node->now()); if ((fromBridged)||(includeCom)) { Packet outp(toZT,RR->identity.address(),Packet::VERB_EXT_FRAME); -- cgit v1.2.3 From 84ba365c77fafd9190d258ec364e9e06b9249764 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Mon, 6 Jul 2015 17:20:41 -0700 Subject: Fix bug in direct path push send. --- node/Peer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'node') diff --git a/node/Peer.cpp b/node/Peer.cpp index f5fdf7dd..7c936d4a 100644 --- a/node/Peer.cpp +++ b/node/Peer.cpp @@ -256,7 +256,7 @@ void Peer::pushDirectPaths(const RuntimeEnvironment *RR,const std::vector outp.append((uint8_t)((p->metric() >= 0) ? ((p->metric() <= 255) ? p->metric() : 255) : 0)); outp.append((uint16_t)0); outp.append(addressType); - outp.append((addressType == 4) ? 6 : 18); + outp.append((uint8_t)((addressType == 4) ? 6 : 18)); outp.append(p->address().rawIpData(),((addressType == 4) ? 4 : 16)); outp.append((uint16_t)p->address().port()); -- cgit v1.2.3 From f881cdd7675fc861ea6696837ab6e8ea650c036e Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Mon, 6 Jul 2015 17:22:37 -0700 Subject: Add new .h file to VS build, and Windows side of local interface address enumeration. --- node/IncomingPacket.cpp | 8 +++-- service/OneService.cpp | 43 +++++++++++++++++++++++-- windows/ZeroTierOne/ZeroTierOne.vcxproj | 1 + windows/ZeroTierOne/ZeroTierOne.vcxproj.filters | 3 ++ 4 files changed, 51 insertions(+), 4 deletions(-) (limited to 'node') diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index 87669ba7..c1594f81 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -899,13 +899,17 @@ bool IncomingPacket::_doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,const Sha switch(addrType) { case 4: { InetAddress a(field(ptr,4),4,at(ptr + 4)); - if ((flags & (0x01 | 0x02)) == 0) + if ((flags & (0x01 | 0x02)) == 0) { + printf("contacting %s at %s\r\n",peer->address().toString().c_str(),a.toString().c_str()); peer->attemptToContactAt(RR,a,RR->node->now()); + } } break; case 6: { InetAddress a(field(ptr,16),16,at(ptr + 16)); - if ((flags & (0x01 | 0x02)) == 0) + if ((flags & (0x01 | 0x02)) == 0) { + printf("contacting %s at %s\r\n",peer->address().toString().c_str(),a.toString().c_str()); peer->attemptToContactAt(RR,a,RR->node->now()); + } } break; } ptr += addrLen; diff --git a/service/OneService.cpp b/service/OneService.cpp index 109050bf..527ac1b0 100644 --- a/service/OneService.cpp +++ b/service/OneService.cpp @@ -75,7 +75,11 @@ class SqliteNetworkController; #endif // ZT_ENABLE_NETWORK_CONTROLLER #ifdef __WINDOWS__ +#include +#include #include +#include +#include #else #include #include @@ -614,10 +618,45 @@ public: { Mutex::Lock _l(_taps_m); for(std::map< uint64_t,EthernetTap *>::const_iterator t(_taps.begin());t!=_taps.end();++t) - ztDevices.push_back(t->second->deviceName()); + ztDevices.push_back(t->second->luid()); } - // TODO + char aabuf[16384]; + ULONG aalen = sizeof(aabuf); + if (GetAdaptersAddresses(AF_UNSPEC,GAA_FLAG_SKIP_ANYCAST|GAA_FLAG_SKIP_MULTICAST|GAA_FLAG_SKIP_DNS_SERVER,(void *)0,reinterpret_cast(aabuf),&aalen) == NO_ERROR) { + PIP_ADAPTER_ADDRESSES a = reinterpret_cast(aabuf); + while (a) { + bool isZT = false; + for(std::vector::const_iterator d(ztDevices.begin());d!=ztDevices.end();++d) { + if (a->Luid.Value == d->Value) { + isZT = true; + break; + } + } + if (!isZT) { + PIP_ADAPTER_UNICAST_ADDRESS ua = a->FirstUnicastAddress; + while (ua) { + InetAddress ip(ua->Address.lpSockaddr); + if ((ip.ss_family == AF_INET)||(ip.ss_family == AF_INET6)) { + switch(ip.ipScope()) { + case InetAddress::IP_SCOPE_LINK_LOCAL: + case InetAddress::IP_SCOPE_PRIVATE: + case InetAddress::IP_SCOPE_PSEUDOPRIVATE: + case InetAddress::IP_SCOPE_SHARED: + case InetAddress::IP_SCOPE_GLOBAL: + ip.setPort(_port); + _node->addLocalInterfaceAddress(reinterpret_cast(&ip),0,ZT1_LOCAL_INTERFACE_ADDRESS_TRUST_NORMAL,0); + break; + default: + break; + } + } + ua = ua->Next; + } + } + a = a->Next; + } + } #endif // __WINDOWS__ } diff --git a/windows/ZeroTierOne/ZeroTierOne.vcxproj b/windows/ZeroTierOne/ZeroTierOne.vcxproj index acd4c7fe..554669b6 100644 --- a/windows/ZeroTierOne/ZeroTierOne.vcxproj +++ b/windows/ZeroTierOne/ZeroTierOne.vcxproj @@ -94,6 +94,7 @@ + diff --git a/windows/ZeroTierOne/ZeroTierOne.vcxproj.filters b/windows/ZeroTierOne/ZeroTierOne.vcxproj.filters index 9337a88e..f36b5dc0 100644 --- a/windows/ZeroTierOne/ZeroTierOne.vcxproj.filters +++ b/windows/ZeroTierOne/ZeroTierOne.vcxproj.filters @@ -344,6 +344,9 @@ Header Files\osdep + + Header Files\node + -- cgit v1.2.3 From 6da9d2d36f5313848d35a4850aaf0f3c5f11d98b Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Mon, 6 Jul 2015 17:23:22 -0700 Subject: Remove debug printf(). --- node/IncomingPacket.cpp | 2 -- 1 file changed, 2 deletions(-) (limited to 'node') diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index c1594f81..ab771b7e 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -900,14 +900,12 @@ bool IncomingPacket::_doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,const Sha case 4: { InetAddress a(field(ptr,4),4,at(ptr + 4)); if ((flags & (0x01 | 0x02)) == 0) { - printf("contacting %s at %s\r\n",peer->address().toString().c_str(),a.toString().c_str()); peer->attemptToContactAt(RR,a,RR->node->now()); } } break; case 6: { InetAddress a(field(ptr,16),16,at(ptr + 16)); if ((flags & (0x01 | 0x02)) == 0) { - printf("contacting %s at %s\r\n",peer->address().toString().c_str(),a.toString().c_str()); peer->attemptToContactAt(RR,a,RR->node->now()); } } break; -- cgit v1.2.3 From f398952a6c03574e5947f6dfe5ea0f7b0f0b5224 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 7 Jul 2015 08:14:41 -0700 Subject: Revert some bad docs in Packet -- I think we will still use that. Also rename addMembershipCertificate to more security-descriptive validateAndAddMembershipCertificate, give it a return value, and drop unused force parameter. --- node/IncomingPacket.cpp | 8 ++++---- node/Network.cpp | 54 +++++++++++++++++++++++++------------------------ node/Network.hpp | 4 ++-- node/Packet.hpp | 6 ------ 4 files changed, 34 insertions(+), 38 deletions(-) (limited to 'node') diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index ab771b7e..80159194 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -429,7 +429,7 @@ bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,const SharedPtr &p offset += com.deserialize(*this,ZT_PROTO_VERB_MULTICAST_FRAME__OK__IDX_COM_AND_GATHER_RESULTS); SharedPtr network(RR->node->network(nwid)); if ((network)&&(com.hasRequiredFields())) - network->addMembershipCertificate(com,false); + network->validateAndAddMembershipCertificate(com); } if ((flags & 0x02) != 0) { @@ -558,7 +558,7 @@ bool IncomingPacket::_doEXT_FRAME(const RuntimeEnvironment *RR,const SharedPtr

addMembershipCertificate(com,false); + network->validateAndAddMembershipCertificate(com); } if (!network->isAllowed(peer->address())) { @@ -648,7 +648,7 @@ bool IncomingPacket::_doNETWORK_MEMBERSHIP_CERTIFICATE(const RuntimeEnvironment if (com.hasRequiredFields()) { SharedPtr network(RR->node->network(com.networkId())); if (network) - network->addMembershipCertificate(com,false); + network->validateAndAddMembershipCertificate(com); } } @@ -806,7 +806,7 @@ bool IncomingPacket::_doMULTICAST_FRAME(const RuntimeEnvironment *RR,const Share CertificateOfMembership com; offset += com.deserialize(*this,ZT_PROTO_VERB_MULTICAST_FRAME_IDX_COM); if (com.hasRequiredFields()) - network->addMembershipCertificate(com,false); + network->validateAndAddMembershipCertificate(com); } // Check membership after we've read any included COM, since diff --git a/node/Network.cpp b/node/Network.cpp index 4414e4d1..84b8d320 100644 --- a/node/Network.cpp +++ b/node/Network.cpp @@ -267,53 +267,55 @@ void Network::requestConfiguration() RR->sw->send(outp,true,_id); } -void Network::addMembershipCertificate(const CertificateOfMembership &cert,bool forceAccept) +bool Network::validateAndAddMembershipCertificate(const CertificateOfMembership &cert) { if (!cert) // sanity check - return; + return false; Mutex::Lock _l(_lock); CertificateOfMembership &old = _membershipCertificates[cert.issuedTo()]; // Nothing to do if the cert hasn't changed -- we get duplicates due to zealous cert pushing if (old == cert) - return; + return true; // but if it's a duplicate of one we already accepted, return is 'true' // Check signature, log and return if cert is invalid - if (!forceAccept) { - if (cert.signedBy() != controller()) { - TRACE("rejected network membership certificate for %.16llx signed by %s: signer not a controller of this network",(unsigned long long)_id,cert.signedBy().toString().c_str()); - return; + if (cert.signedBy() != controller()) { + TRACE("rejected network membership certificate for %.16llx signed by %s: signer not a controller of this network",(unsigned long long)_id,cert.signedBy().toString().c_str()); + return false; // invalid signer + } + + if (cert.signedBy() == RR->identity.address()) { + + // We are the controller: RR->identity.address() == controller() == cert.signedBy() + // So, verify that we signed th cert ourself + if (!cert.verify(RR->identity)) { + TRACE("rejected network membership certificate for %.16llx self signed by %s: signature check failed",(unsigned long long)_id,cert.signedBy().toString().c_str()); + return false; // invalid signature } - if (cert.signedBy() == RR->identity.address()) { - // We are the controller: RR->identity.address() == controller() == cert.signedBy() - // So, verify that we signed th cert ourself - if (!cert.verify(RR->identity)) { - TRACE("rejected network membership certificate for %.16llx self signed by %s: signature check failed",(unsigned long long)_id,cert.signedBy().toString().c_str()); - return; - } - } else { + } else { - SharedPtr signer(RR->topology->getPeer(cert.signedBy())); + SharedPtr signer(RR->topology->getPeer(cert.signedBy())); - if (!signer) { - // This would be rather odd, since this is our controller... could happen - // if we get packets before we've gotten config. - RR->sw->requestWhois(cert.signedBy()); - return; - } + if (!signer) { + // This would be rather odd, since this is our controller... could happen + // if we get packets before we've gotten config. + RR->sw->requestWhois(cert.signedBy()); + return false; // signer unknown + } - if (!cert.verify(signer->identity())) { - TRACE("rejected network membership certificate for %.16llx signed by %s: signature check failed",(unsigned long long)_id,cert.signedBy().toString().c_str()); - return; - } + if (!cert.verify(signer->identity())) { + TRACE("rejected network membership certificate for %.16llx signed by %s: signature check failed",(unsigned long long)_id,cert.signedBy().toString().c_str()); + return false; // invalid signature } } // If we made it past authentication, update cert if (cert.revision() != old.revision()) old = cert; + + return true; } bool Network::peerNeedsOurMembershipCertificate(const Address &to,uint64_t now) diff --git a/node/Network.hpp b/node/Network.hpp index daa4554e..53a22dcd 100644 --- a/node/Network.hpp +++ b/node/Network.hpp @@ -179,9 +179,9 @@ public: * Add or update a membership certificate * * @param cert Certificate of membership - * @param forceAccept If true, accept without validating signature + * @return True if certificate was accepted as valid */ - void addMembershipCertificate(const CertificateOfMembership &cert,bool forceAccept); + bool validateAndAddMembershipCertificate(const CertificateOfMembership &cert); /** * Check if we should push membership certificate to a peer, and update last pushed diff --git a/node/Packet.hpp b/node/Packet.hpp index 50471b06..51a241ba 100644 --- a/node/Packet.hpp +++ b/node/Packet.hpp @@ -680,12 +680,6 @@ public: * * Certificate contains network ID, peer it was issued for, etc. * - * Note that in the current code base, separate COM pushes are not done. - * Instead the "bundled COM" options are utilized in EXT_FRAME and - * MULTICAST_FRAME to push the COM along with frames. This is slightly - * more efficient. But we'll keep this simple message around in case we - * want to use it in the future. - * * OK/ERROR are not generated. */ VERB_NETWORK_MEMBERSHIP_CERTIFICATE = 10, -- cgit v1.2.3 From c863ff3f02e9d68eb9bea32160d252eaddb7f1f5 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 7 Jul 2015 08:54:48 -0700 Subject: A bunch of comments and cleanup, including some to yesterday's direct path pushing changes. Move path viability check to one place, and stop trying to use link-local addresses since they are not reliable. --- include/ZeroTierOne.h | 9 ++++++- node/IncomingPacket.cpp | 66 +++++++++++++++++++++++++++++++++++-------------- node/Node.cpp | 24 +++++++++++------- node/Node.hpp | 2 +- node/Packet.hpp | 7 +++--- node/Path.hpp | 33 +++++++++++++++++++++++++ service/OneService.cpp | 32 +++--------------------- 7 files changed, 112 insertions(+), 61 deletions(-) (limited to 'node') diff --git a/include/ZeroTierOne.h b/include/ZeroTierOne.h index 4790795e..446bbc77 100644 --- a/include/ZeroTierOne.h +++ b/include/ZeroTierOne.h @@ -978,12 +978,19 @@ void ZT1_Node_freeQueryResult(ZT1_Node *node,void *qr); * Take care that these are never ZeroTier interface addresses, otherwise * strange things might happen or they simply won't work. * + * This returns a boolean indicating whether or not the address was + * accepted. ZeroTier will only communicate over certain address types + * and (for IP) address classes. Thus it's safe to just dump your OS's + * entire remote IP list (excluding ZeroTier interface IPs) into here + * and let ZeroTier determine which addresses it will use. + * * @param addr Local interface address * @param metric Local interface metric * @param trust How much do you trust the local network under this interface? * @param reliable If nonzero, this interface doesn't link to anything behind a NAT or stateful firewall + * @return Boolean: non-zero if address was accepted and added */ -void ZT1_Node_addLocalInterfaceAddress(ZT1_Node *node,const struct sockaddr_storage *addr,int metric,ZT1_LocalInterfaceAddressTrust trust,int reliable); +int ZT1_Node_addLocalInterfaceAddress(ZT1_Node *node,const struct sockaddr_storage *addr,int metric,ZT1_LocalInterfaceAddressTrust trust,int reliable); /** * Clear local interface addresses diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index 80159194..7e883221 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -134,6 +134,9 @@ bool IncomingPacket::_doERROR(const RuntimeEnvironment *RR,const SharedPtr break; case Packet::ERROR_NEED_MEMBERSHIP_CERTIFICATE: { + /* Note: certificates are public so it's safe to push them to anyone + * who asks. We won't communicate unless we also get a certificate + * from the remote that agrees. */ SharedPtr network(RR->node->network(at(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD))); if (network) { SharedPtr nconf(network->config2()); @@ -170,8 +173,20 @@ bool IncomingPacket::_doERROR(const RuntimeEnvironment *RR,const SharedPtr bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR) { + /* Note: this is the only packet ever sent in the clear, and it's also + * the only packet that we authenticate via a different path. Authentication + * occurs here and is based on the validity of the identity and the + * integrity of the packet's MAC, but it must be done after we check + * the identity since HELLO is a mechanism for learning new identities + * in the first place. */ + try { const unsigned int protoVersion = (*this)[ZT_PROTO_VERB_HELLO_IDX_PROTOCOL_VERSION]; + if (protoVersion < ZT_PROTO_VERSION_MIN) { + TRACE("dropped HELLO from %s(%s): protocol version too old",id.address().toString().c_str(),_remoteAddress.toString().c_str()); + return true; + } + const unsigned int vMajor = (*this)[ZT_PROTO_VERB_HELLO_IDX_MAJOR_VERSION]; const unsigned int vMinor = (*this)[ZT_PROTO_VERB_HELLO_IDX_MINOR_VERSION]; const unsigned int vRevision = at(ZT_PROTO_VERB_HELLO_IDX_REVISION); @@ -179,6 +194,10 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR) Identity id; unsigned int destAddrPtr = id.deserialize(*this,ZT_PROTO_VERB_HELLO_IDX_IDENTITY) + ZT_PROTO_VERB_HELLO_IDX_IDENTITY; + if (source() != id.address()) { + TRACE("dropped HELLO from %s(%s): identity not for sending address",source().toString().c_str(),_remoteAddress.toString().c_str()); + return true; + } InetAddress destAddr; if (destAddrPtr < size()) { // ZeroTier One < 1.0.3 did not include this field @@ -193,16 +212,6 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR) } } - if (source() != id.address()) { - TRACE("dropped HELLO from %s(%s): identity not for sending address",source().toString().c_str(),_remoteAddress.toString().c_str()); - return true; - } - - if (protoVersion < ZT_PROTO_VERSION_MIN) { - TRACE("dropped HELLO from %s(%s): protocol version too old",id.address().toString().c_str(),_remoteAddress.toString().c_str()); - return true; - } - SharedPtr peer(RR->topology->getPeer(id.address())); if (peer) { // We already have an identity with this address -- check for collisions @@ -245,12 +254,14 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR) } else { // We don't already have an identity with this address -- validate and learn it + // Check identity proof of work if (!id.locallyValidate()) { RR->node->postEvent(ZT1_EVENT_AUTHENTICATION_FAILURE,(const void *)&_remoteAddress); TRACE("dropped HELLO from %s(%s): identity invalid",id.address().toString().c_str(),_remoteAddress.toString().c_str()); return true; } + // Check packet integrity and authentication SharedPtr newPeer(new Peer(RR->identity,id)); if (!dearmor(newPeer->key())) { RR->node->postEvent(ZT1_EVENT_AUTHENTICATION_FAILURE,(const void *)&_remoteAddress); @@ -554,14 +565,17 @@ bool IncomingPacket::_doEXT_FRAME(const RuntimeEnvironment *RR,const SharedPtr

validateAndAddMembershipCertificate(com); + if (com.hasRequiredFields()) { + if (!network->validateAndAddMembershipCertificate(com)) + comFailed = true; // technically this check is redundant to isAllowed(), but do it anyway for thoroughness + } } - if (!network->isAllowed(peer->address())) { + if ((comFailed)||(!network->isAllowed(peer->address()))) { TRACE("dropped EXT_FRAME from %s(%s): not a member of private network %.16llx",peer->address().toString().c_str(),_remoteAddress.toString().c_str(),network->id()); _sendErrorNeedCertificate(RR,peer,network->id()); return true; @@ -647,8 +661,21 @@ bool IncomingPacket::_doNETWORK_MEMBERSHIP_CERTIFICATE(const RuntimeEnvironment ptr += com.deserialize(*this,ptr); if (com.hasRequiredFields()) { SharedPtr network(RR->node->network(com.networkId())); - if (network) - network->validateAndAddMembershipCertificate(com); + if (network) { + if (network->validateAndAddMembershipCertificate(com)) { + if ((network->isAllowed(peer->address()))&&(network->peerNeedsOurMembershipCertificate(peer->address(),RR->node->now()))) { + // If peer passed our check and we haven't sent it our cert yet, respond + // and push our cert as well for instant authorization setup. + SharedPtr nconf(network->config2()); + if ((nconf)&&(nconf->com())) { + Packet outp(peer->address(),RR->identity.address(),Packet::VERB_NETWORK_MEMBERSHIP_CERTIFICATE); + nconf->com().serialize(outp); + outp.armor(peer->key(),true); + RR->node->putPacket(_remoteAddress,outp.data(),outp.size()); + } + } + } + } } } @@ -890,24 +917,25 @@ bool IncomingPacket::_doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,const Sha unsigned int ptr = ZT_PACKET_IDX_PAYLOAD + 2; while (count) { // if ptr overflows Buffer will throw + // TODO: properly handle blacklisting, support other features... see Packet.hpp. + unsigned int flags = (*this)[ptr++]; /*int metric = (*this)[ptr++];*/ ++ptr; unsigned int extLen = at(ptr); ptr += 2; ptr += extLen; // unused right now unsigned int addrType = (*this)[ptr++]; + unsigned int addrLen = (*this)[ptr++]; switch(addrType) { case 4: { InetAddress a(field(ptr,4),4,at(ptr + 4)); - if ((flags & (0x01 | 0x02)) == 0) { + if ( ((flags & (0x01 | 0x02)) == 0) && (Path::isAddressValidForPath(a)) ) peer->attemptToContactAt(RR,a,RR->node->now()); - } } break; case 6: { InetAddress a(field(ptr,16),16,at(ptr + 16)); - if ((flags & (0x01 | 0x02)) == 0) { + if ( ((flags & (0x01 | 0x02)) == 0) && (Path::isAddressValidForPath(a)) ) peer->attemptToContactAt(RR,a,RR->node->now()); - } } break; } ptr += addrLen; diff --git a/node/Node.cpp b/node/Node.cpp index 057dc285..d8bd8910 100644 --- a/node/Node.cpp +++ b/node/Node.cpp @@ -426,12 +426,16 @@ void Node::freeQueryResult(void *qr) ::free(qr); } -void Node::addLocalInterfaceAddress(const struct sockaddr_storage *addr,int metric,ZT1_LocalInterfaceAddressTrust trust,int reliable) -{ - Mutex::Lock _l(_directPaths_m); - _directPaths.push_back(Path(*(reinterpret_cast(addr)),metric,(Path::Trust)trust,reliable != 0)); - std::sort(_directPaths.begin(),_directPaths.end()); - _directPaths.erase(std::unique(_directPaths.begin(),_directPaths.end()),_directPaths.end()); +int Node::addLocalInterfaceAddress(const struct sockaddr_storage *addr,int metric,ZT1_LocalInterfaceAddressTrust trust,int reliable) +{ + if (Path::isAddressValidForPath(*(reinterpret_cast(addr)))) { + Mutex::Lock _l(_directPaths_m); + _directPaths.push_back(Path(*(reinterpret_cast(addr)),metric,(Path::Trust)trust,reliable != 0)); + std::sort(_directPaths.begin(),_directPaths.end()); + _directPaths.erase(std::unique(_directPaths.begin(),_directPaths.end()),_directPaths.end()); + return 1; + } + return 0; } void Node::clearLocalInterfaceAddresses() @@ -693,11 +697,13 @@ void ZT1_Node_setNetconfMaster(ZT1_Node *node,void *networkControllerInstance) } catch ( ... ) {} } -void ZT1_Node_addLocalInterfaceAddress(ZT1_Node *node,const struct sockaddr_storage *addr,int metric,ZT1_LocalInterfaceAddressTrust trust,int reliable) +int ZT1_Node_addLocalInterfaceAddress(ZT1_Node *node,const struct sockaddr_storage *addr,int metric,ZT1_LocalInterfaceAddressTrust trust,int reliable) { try { - reinterpret_cast(node)->addLocalInterfaceAddress(addr,metric,trust,reliable); - } catch ( ... ) {} + return reinterpret_cast(node)->addLocalInterfaceAddress(addr,metric,trust,reliable); + } catch ( ... ) { + return 0; + } } void ZT1_Node_clearLocalInterfaceAddresses(ZT1_Node *node) diff --git a/node/Node.hpp b/node/Node.hpp index 1c260545..fe31576c 100644 --- a/node/Node.hpp +++ b/node/Node.hpp @@ -104,7 +104,7 @@ public: ZT1_VirtualNetworkConfig *networkConfig(uint64_t nwid) const; ZT1_VirtualNetworkList *networks() const; void freeQueryResult(void *qr); - void addLocalInterfaceAddress(const struct sockaddr_storage *addr,int metric,ZT1_LocalInterfaceAddressTrust trust,int reliable); + int addLocalInterfaceAddress(const struct sockaddr_storage *addr,int metric,ZT1_LocalInterfaceAddressTrust trust,int reliable); void clearLocalInterfaceAddresses(); void setNetconfMaster(void *networkControllerInstance); diff --git a/node/Packet.hpp b/node/Packet.hpp index 51a241ba..9787edb7 100644 --- a/node/Packet.hpp +++ b/node/Packet.hpp @@ -678,9 +678,10 @@ public: * <[...] serialized certificate of membership> * [ ... additional certificates may follow ...] * - * Certificate contains network ID, peer it was issued for, etc. - * - * OK/ERROR are not generated. + * OK/ERROR are not generated, but the recipient should push its network + * membership certificate if the certificate the peer pushed is valid + * and agrees and if it hasn't done so in too long. This ensures instant + * network authentication setup between valid and authorized peers. */ VERB_NETWORK_MEMBERSHIP_CERTIFICATE = 10, diff --git a/node/Path.hpp b/node/Path.hpp index 80b9a3c0..cd21444b 100644 --- a/node/Path.hpp +++ b/node/Path.hpp @@ -94,6 +94,39 @@ public: inline bool operator<=(const Path &p) const throw() { return (_addr <= p._addr); } inline bool operator>=(const Path &p) const throw() { return (_addr >= p._addr); } + /** + * Check whether this address is valid for a ZeroTier path + * + * This checks the address type and scope against address types and scopes + * that we currently support for ZeroTier communication. + * + * @param a Address to check + * @return True if address is good for ZeroTier path use + */ + static inline bool isAddressValidForPath(const InetAddress &a) + throw() + { + if ((a.ss_family == AF_INET)||(a.ss_family == AF_INET6)) { + switch(a.ipScope()) { + /* Note: we don't do link-local at the moment. Unfortunately these + * cause several issues. The first is that they usually require a + * device qualifier, which we don't handle yet and can't portably + * push in PUSH_DIRECT_PATHS. The second is that some OSes assign + * these very ephemerally or otherwise strangely. So we'll use + * private, pseudo-private, shared (e.g. carrier grade NAT), or + * global IP addresses. */ + case InetAddress::IP_SCOPE_PRIVATE: + case InetAddress::IP_SCOPE_PSEUDOPRIVATE: + case InetAddress::IP_SCOPE_SHARED: + case InetAddress::IP_SCOPE_GLOBAL: + return true; + default: + return false; + } + } + return false; + } + protected: InetAddress _addr; int _metric; // negative == blacklisted diff --git a/service/OneService.cpp b/service/OneService.cpp index 527ac1b0..bde59d56 100644 --- a/service/OneService.cpp +++ b/service/OneService.cpp @@ -591,20 +591,8 @@ public: } if (!isZT) { InetAddress ip(ifa->ifa_addr); - if ((ip.ss_family == AF_INET)||(ip.ss_family == AF_INET6)) { - switch(ip.ipScope()) { - case InetAddress::IP_SCOPE_LINK_LOCAL: - case InetAddress::IP_SCOPE_PRIVATE: - case InetAddress::IP_SCOPE_PSEUDOPRIVATE: - case InetAddress::IP_SCOPE_SHARED: - case InetAddress::IP_SCOPE_GLOBAL: - ip.setPort(_port); - _node->addLocalInterfaceAddress(reinterpret_cast(&ip),0,ZT1_LOCAL_INTERFACE_ADDRESS_TRUST_NORMAL,0); - break; - default: - break; - } - } + ip.setPort(_port); + _node->addLocalInterfaceAddress(reinterpret_cast(&ip),0,ZT1_LOCAL_INTERFACE_ADDRESS_TRUST_NORMAL,0); } } ifa = ifa->ifa_next; @@ -637,20 +625,8 @@ public: PIP_ADAPTER_UNICAST_ADDRESS ua = a->FirstUnicastAddress; while (ua) { InetAddress ip(ua->Address.lpSockaddr); - if ((ip.ss_family == AF_INET)||(ip.ss_family == AF_INET6)) { - switch(ip.ipScope()) { - case InetAddress::IP_SCOPE_LINK_LOCAL: - case InetAddress::IP_SCOPE_PRIVATE: - case InetAddress::IP_SCOPE_PSEUDOPRIVATE: - case InetAddress::IP_SCOPE_SHARED: - case InetAddress::IP_SCOPE_GLOBAL: - ip.setPort(_port); - _node->addLocalInterfaceAddress(reinterpret_cast(&ip),0,ZT1_LOCAL_INTERFACE_ADDRESS_TRUST_NORMAL,0); - break; - default: - break; - } - } + ip.setPort(_port); + _node->addLocalInterfaceAddress(reinterpret_cast(&ip),0,ZT1_LOCAL_INTERFACE_ADDRESS_TRUST_NORMAL,0); ua = ua->Next; } } -- cgit v1.2.3 From 778c7e6e703353030e2ea130e3db7cc968a5d53c Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 7 Jul 2015 10:00:34 -0700 Subject: More cleanup to direct path push, comment fixes, etc. --- node/IncomingPacket.cpp | 17 +------- node/Network.hpp | 2 +- node/OutboundMulticast.cpp | 8 +--- node/Packet.hpp | 8 ++-- node/Peer.cpp | 10 +++-- node/Peer.hpp | 8 ++-- node/Switch.cpp | 98 +++++++++++++++++++++++----------------------- node/Switch.hpp | 5 ++- 8 files changed, 71 insertions(+), 85 deletions(-) (limited to 'node') diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index 7e883221..d5b4e9e6 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -661,21 +661,8 @@ bool IncomingPacket::_doNETWORK_MEMBERSHIP_CERTIFICATE(const RuntimeEnvironment ptr += com.deserialize(*this,ptr); if (com.hasRequiredFields()) { SharedPtr network(RR->node->network(com.networkId())); - if (network) { - if (network->validateAndAddMembershipCertificate(com)) { - if ((network->isAllowed(peer->address()))&&(network->peerNeedsOurMembershipCertificate(peer->address(),RR->node->now()))) { - // If peer passed our check and we haven't sent it our cert yet, respond - // and push our cert as well for instant authorization setup. - SharedPtr nconf(network->config2()); - if ((nconf)&&(nconf->com())) { - Packet outp(peer->address(),RR->identity.address(),Packet::VERB_NETWORK_MEMBERSHIP_CERTIFICATE); - nconf->com().serialize(outp); - outp.armor(peer->key(),true); - RR->node->putPacket(_remoteAddress,outp.data(),outp.size()); - } - } - } - } + if (network) + network->validateAndAddMembershipCertificate(com); } } diff --git a/node/Network.hpp b/node/Network.hpp index 53a22dcd..d7320d46 100644 --- a/node/Network.hpp +++ b/node/Network.hpp @@ -184,7 +184,7 @@ public: bool validateAndAddMembershipCertificate(const CertificateOfMembership &cert); /** - * Check if we should push membership certificate to a peer, and update last pushed + * Check if we should push membership certificate to a peer, AND update last pushed * * If we haven't pushed a cert to this peer in a long enough time, this returns * true and updates the last pushed time. Otherwise it returns false. diff --git a/node/OutboundMulticast.cpp b/node/OutboundMulticast.cpp index 5809504a..46116c07 100644 --- a/node/OutboundMulticast.cpp +++ b/node/OutboundMulticast.cpp @@ -102,13 +102,9 @@ void OutboundMulticast::init( void OutboundMulticast::sendOnly(const RuntimeEnvironment *RR,const Address &toAddr) { - SharedPtr network(RR->node->network(_nwid)); - - if (!network) - return; - if (_haveCom) { - if (network->peerNeedsOurMembershipCertificate(toAddr,RR->node->now())) { + SharedPtr network(RR->node->network(_nwid)); + if ((network)&&(network->peerNeedsOurMembershipCertificate(toAddr,RR->node->now()))) { _packetWithCom.newInitializationVector(); _packetWithCom.setDestination(toAddr); //TRACE(">>MC %.16llx -> %s (with COM)",(unsigned long long)this,toAddr.toString().c_str()); diff --git a/node/Packet.hpp b/node/Packet.hpp index 9787edb7..e84306c2 100644 --- a/node/Packet.hpp +++ b/node/Packet.hpp @@ -678,10 +678,10 @@ public: * <[...] serialized certificate of membership> * [ ... additional certificates may follow ...] * - * OK/ERROR are not generated, but the recipient should push its network - * membership certificate if the certificate the peer pushed is valid - * and agrees and if it hasn't done so in too long. This ensures instant - * network authentication setup between valid and authorized peers. + * This is sent in response to ERROR_NEED_MEMBERSHIP_CERTIFICATE and may + * be pushed at any other time to keep exchanged certificates up to date. + * + * OK/ERROR are not generated. */ VERB_NETWORK_MEMBERSHIP_CERTIFICATE = 10, diff --git a/node/Peer.cpp b/node/Peer.cpp index 7c936d4a..84aa8bef 100644 --- a/node/Peer.cpp +++ b/node/Peer.cpp @@ -208,12 +208,13 @@ void Peer::doPingAndKeepalive(const RuntimeEnvironment *RR,uint64_t now) } } -void Peer::pushDirectPaths(const RuntimeEnvironment *RR,const std::vector &dps,uint64_t now,bool force) +void Peer::pushDirectPaths(const RuntimeEnvironment *RR,RemotePath *path,uint64_t now,bool force) { - if ((!dps.empty())&&(((now - _lastDirectPathPush) >= ZT_DIRECT_PATH_PUSH_INTERVAL)||(force))) { + if ((((now - _lastDirectPathPush) >= ZT_DIRECT_PATH_PUSH_INTERVAL)||(force))) { _lastDirectPathPush = now; - TRACE("pushing %u direct paths to %s",(unsigned int)dps.size(),_id.address().toString().c_str()); + std::vector dps(RR->node->directPaths()); + TRACE("pushing %u direct paths (local interface addresses) to %s",(unsigned int)dps.size(),_id.address().toString().c_str()); std::vector::const_iterator p(dps.begin()); while (p != dps.end()) { @@ -266,7 +267,8 @@ void Peer::pushDirectPaths(const RuntimeEnvironment *RR,const std::vector if (count) { outp.setAt(ZT_PACKET_IDX_PAYLOAD,(uint16_t)count); - RR->sw->send(outp,true,0); + outp.armor(_key,true); + path->send(RR,outp.data(),outp.size(),now); } } } diff --git a/node/Peer.hpp b/node/Peer.hpp index 2af3b9e6..f5118794 100644 --- a/node/Peer.hpp +++ b/node/Peer.hpp @@ -179,14 +179,14 @@ public: void doPingAndKeepalive(const RuntimeEnvironment *RR,uint64_t now); /** - * Push direct paths (if within rate limit) + * Push direct paths if we haven't done so in [rate limit] milliseconds * * @param RR Runtime environment - * @param dps Direct paths to me to push to this peer + * @param path Remote path to use to send the push * @param now Current time - * @param force If true, force regardless of when we pushed direct paths last + * @param force If true, push regardless of rate limit */ - void pushDirectPaths(const RuntimeEnvironment *RR,const std::vector &dps,uint64_t now,bool force); + void pushDirectPaths(const RuntimeEnvironment *RR,RemotePath *path,uint64_t now,bool force); /** * @return All known direct paths to this peer diff --git a/node/Switch.cpp b/node/Switch.cpp index 201f36d1..13070be1 100644 --- a/node/Switch.cpp +++ b/node/Switch.cpp @@ -162,38 +162,34 @@ void Switch::onLocalEthernet(const SharedPtr &network,const MAC &from,c if (to[0] == MAC::firstOctetForNetwork(network->id())) { // Destination is another ZeroTier peer on the same network - Address toZT(to.toAddress(network->id())); - if (network->isAllowed(toZT)) { - const bool includeCom = network->peerNeedsOurMembershipCertificate(toZT,RR->node->now()); - if ((fromBridged)||(includeCom)) { - Packet outp(toZT,RR->identity.address(),Packet::VERB_EXT_FRAME); - outp.append(network->id()); - if (includeCom) { - outp.append((unsigned char)0x01); // 0x01 -- COM included - nconf->com().serialize(outp); - } else { - outp.append((unsigned char)0x00); - } - to.appendTo(outp); - from.appendTo(outp); - outp.append((uint16_t)etherType); - outp.append(data,len); - outp.compress(); - send(outp,true,network->id()); + Address toZT(to.toAddress(network->id())); // since in-network MACs are derived from addresses and network IDs, we can reverse this + const bool includeCom = network->peerNeedsOurMembershipCertificate(toZT,RR->node->now()); + if ((fromBridged)||(includeCom)) { + Packet outp(toZT,RR->identity.address(),Packet::VERB_EXT_FRAME); + outp.append(network->id()); + if (includeCom) { + outp.append((unsigned char)0x01); // 0x01 -- COM included + nconf->com().serialize(outp); } else { - Packet outp(toZT,RR->identity.address(),Packet::VERB_FRAME); - outp.append(network->id()); - outp.append((uint16_t)etherType); - outp.append(data,len); - outp.compress(); - send(outp,true,network->id()); + outp.append((unsigned char)0x00); } - - //TRACE("%.16llx: UNICAST: %s -> %s etherType==%s(%.4x) vlanId==%u len==%u fromBridged==%d includeCom==%d",network->id(),from.toString().c_str(),to.toString().c_str(),etherTypeName(etherType),etherType,vlanId,len,(int)fromBridged,(int)includeCom); + to.appendTo(outp); + from.appendTo(outp); + outp.append((uint16_t)etherType); + outp.append(data,len); + outp.compress(); + send(outp,true,network->id()); } else { - TRACE("%.16llx: UNICAST: %s -> %s etherType==%s dropped, destination not a member of private network",network->id(),from.toString().c_str(),to.toString().c_str(),etherTypeName(etherType)); + Packet outp(toZT,RR->identity.address(),Packet::VERB_FRAME); + outp.append(network->id()); + outp.append((uint16_t)etherType); + outp.append(data,len); + outp.compress(); + send(outp,true,network->id()); } + //TRACE("%.16llx: UNICAST: %s -> %s etherType==%s(%.4x) vlanId==%u len==%u fromBridged==%d includeCom==%d",network->id(),from.toString().c_str(),to.toString().c_str(),etherTypeName(etherType),etherType,vlanId,len,(int)fromBridged,(int)includeCom); + return; } @@ -205,7 +201,7 @@ void Switch::onLocalEthernet(const SharedPtr &network,const MAC &from,c /* Create an array of up to ZT_MAX_BRIDGE_SPAM recipients for this bridged frame. */ bridges[0] = network->findBridgeTo(to); - if ((bridges[0])&&(bridges[0] != RR->identity.address())&&(network->isAllowed(bridges[0]))&&(network->permitsBridging(bridges[0]))) { + if ((bridges[0])&&(bridges[0] != RR->identity.address())&&(network->permitsBridging(bridges[0]))) { /* We have a known bridge route for this MAC, send it there. */ ++numBridges; } else if (!nconf->activeBridges().empty()) { @@ -215,8 +211,7 @@ void Switch::onLocalEthernet(const SharedPtr &network,const MAC &from,c if (nconf->activeBridges().size() <= ZT_MAX_BRIDGE_SPAM) { // If there are <= ZT_MAX_BRIDGE_SPAM active bridges, spam them all while (ab != nconf->activeBridges().end()) { - if (network->isAllowed(*ab)) // config sanity check - bridges[numBridges++] = *ab; + bridges[numBridges++] = *ab; ++ab; } } else { @@ -225,8 +220,7 @@ void Switch::onLocalEthernet(const SharedPtr &network,const MAC &from,c if (ab == nconf->activeBridges().end()) ab = nconf->activeBridges().begin(); if (((unsigned long)RR->prng->next32() % (unsigned long)nconf->activeBridges().size()) == 0) { - if (network->isAllowed(*ab)) // config sanity check - bridges[numBridges++] = *ab; + bridges[numBridges++] = *ab; ++ab; } else ++ab; } @@ -703,29 +697,28 @@ bool Switch::_trySend(const Packet &packet,bool encrypt,uint64_t nwid) if (peer) { const uint64_t now = RR->node->now(); + SharedPtr network; + SharedPtr nconf; if (nwid) { - // If this packet has an associated network, give the peer additional hints for direct connectivity - peer->pushDirectPaths(RR,RR->node->directPaths(),now,false); + network = RR->node->network(nwid); + if (!network) + return false; // we probably just left this network, let its packets die + nconf = network->config2(); + if (!nconf) + return false; // sanity check: unconfigured network? why are we trying to talk to it? } RemotePath *viaPath = peer->getBestPath(now); + SharedPtr relay; if (!viaPath) { - SharedPtr relay; - // See if this network has a preferred relay (if packet has an associated network) - if (nwid) { - SharedPtr network(RR->node->network(nwid)); - if (network) { - SharedPtr nconf(network->config2()); - if (nconf) { - unsigned int latency = ~((unsigned int)0); - for(std::vector< std::pair >::const_iterator r(nconf->relays().begin());r!=nconf->relays().end();++r) { - if (r->first != peer->address()) { - SharedPtr rp(RR->topology->getPeer(r->first)); - if ((rp)&&(rp->hasActiveDirectPath(now))&&(rp->latency() <= latency)) - rp.swap(relay); - } - } + if (nconf) { + unsigned int latency = ~((unsigned int)0); + for(std::vector< std::pair >::const_iterator r(nconf->relays().begin());r!=nconf->relays().end();++r) { + if (r->first != peer->address()) { + SharedPtr rp(RR->topology->getPeer(r->first)); + if ((rp)&&(rp->hasActiveDirectPath(now))&&(rp->latency() <= latency)) + rp.swap(relay); } } } @@ -735,7 +728,12 @@ bool Switch::_trySend(const Packet &packet,bool encrypt,uint64_t nwid) relay = RR->topology->getBestRoot(); if (!(relay)||(!(viaPath = relay->getBestPath(now)))) - return false; + return false; // no paths, no root servers? + } + + if ((network)&&(relay)&&(network->isAllowed(peer->address()))) { + // Push hints for direct connectivity to this peer if we are relaying + peer->pushDirectPaths(RR,viaPath,now,false); } Packet tmp(packet); diff --git a/node/Switch.hpp b/node/Switch.hpp index af6e5938..95ca362c 100644 --- a/node/Switch.hpp +++ b/node/Switch.hpp @@ -108,10 +108,13 @@ public: * * Needless to say, the packet's source must be this node. Otherwise it * won't be encrypted right. (This is not used for relaying.) + * + * The network ID should only be specified for frames and other actual + * network traffic. * * @param packet Packet to send * @param encrypt Encrypt packet payload? (always true except for HELLO) - * @param nwid Network ID or 0 if message is not related to a specific network + * @param nwid Related network ID or 0 if message is not in-network traffic */ void send(const Packet &packet,bool encrypt,uint64_t nwid); -- cgit v1.2.3 From 07ea4fd4f92d7d5aa8573525b11bf7821b9acb19 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 7 Jul 2015 10:02:48 -0700 Subject: Fix potential bug in controller config request. --- node/Network.cpp | 2 +- node/Switch.hpp | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'node') diff --git a/node/Network.cpp b/node/Network.cpp index 84b8d320..adc8e1b8 100644 --- a/node/Network.cpp +++ b/node/Network.cpp @@ -264,7 +264,7 @@ void Network::requestConfiguration() outp.append((uint64_t)_config->revision()); else outp.append((uint64_t)0); } - RR->sw->send(outp,true,_id); + RR->sw->send(outp,true,0); } bool Network::validateAndAddMembershipCertificate(const CertificateOfMembership &cert) diff --git a/node/Switch.hpp b/node/Switch.hpp index 95ca362c..1b29d050 100644 --- a/node/Switch.hpp +++ b/node/Switch.hpp @@ -110,7 +110,8 @@ public: * won't be encrypted right. (This is not used for relaying.) * * The network ID should only be specified for frames and other actual - * network traffic. + * network traffic. Other traffic such as controller requests and regular + * protocol messages should specify zero. * * @param packet Packet to send * @param encrypt Encrypt packet payload? (always true except for HELLO) -- cgit v1.2.3 From 41fc08b3308be6ac57d737b225a58d41854bd695 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 7 Jul 2015 10:06:05 -0700 Subject: etherTypeName() is only used in Switch and only with ZT_TRACE --- node/Switch.cpp | 33 +++++++++++++++++---------------- node/Switch.hpp | 7 ------- 2 files changed, 17 insertions(+), 23 deletions(-) (limited to 'node') diff --git a/node/Switch.cpp b/node/Switch.cpp index 13070be1..62fa02cd 100644 --- a/node/Switch.cpp +++ b/node/Switch.cpp @@ -48,6 +48,23 @@ namespace ZeroTier { +#ifdef ZT_TRACE +static const char *etherTypeName(const unsigned int etherType) +{ + switch(etherType) { + case ZT_ETHERTYPE_IPV4: return "IPV4"; + case ZT_ETHERTYPE_ARP: return "ARP"; + case ZT_ETHERTYPE_RARP: return "RARP"; + case ZT_ETHERTYPE_ATALK: return "ATALK"; + case ZT_ETHERTYPE_AARP: return "AARP"; + case ZT_ETHERTYPE_IPX_A: return "IPX_A"; + case ZT_ETHERTYPE_IPX_B: return "IPX_B"; + case ZT_ETHERTYPE_IPV6: return "IPV6"; + } + return "UNKNOWN"; +} +#endif // ZT_TRACE + Switch::Switch(const RuntimeEnvironment *renv) : RR(renv) { @@ -519,22 +536,6 @@ unsigned long Switch::doTimerTasks(uint64_t now) return nextDelay; } -const char *Switch::etherTypeName(const unsigned int etherType) - throw() -{ - switch(etherType) { - case ZT_ETHERTYPE_IPV4: return "IPV4"; - case ZT_ETHERTYPE_ARP: return "ARP"; - case ZT_ETHERTYPE_RARP: return "RARP"; - case ZT_ETHERTYPE_ATALK: return "ATALK"; - case ZT_ETHERTYPE_AARP: return "AARP"; - case ZT_ETHERTYPE_IPX_A: return "IPX_A"; - case ZT_ETHERTYPE_IPX_B: return "IPX_B"; - case ZT_ETHERTYPE_IPV6: return "IPV6"; - } - return "UNKNOWN"; -} - void Switch::_handleRemotePacketFragment(const InetAddress &fromAddr,const void *data,unsigned int len) { Packet::Fragment fragment(data,len); diff --git a/node/Switch.hpp b/node/Switch.hpp index 1b29d050..89c9a56b 100644 --- a/node/Switch.hpp +++ b/node/Switch.hpp @@ -177,13 +177,6 @@ public: */ unsigned long doTimerTasks(uint64_t now); - /** - * @param etherType Ethernet type ID - * @return Human-readable name - */ - static const char *etherTypeName(const unsigned int etherType) - throw(); - private: void _handleRemotePacketFragment(const InetAddress &fromAddr,const void *data,unsigned int len); void _handleRemotePacketHead(const InetAddress &fromAddr,const void *data,unsigned int len); -- cgit v1.2.3 From 3f567a07ca0cda5c3370105d88cb939ad62b4b7d Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 7 Jul 2015 10:49:50 -0700 Subject: Save a little bit of RAM by getting rid of overkill CMWC4096 non-crypto PRNG and replacing it with a simple non-crypto PRNG that just uses Salsa20. --- node/CMWC4096.hpp | 91 --------------------------------------------- node/Multicaster.cpp | 5 +-- node/Node.cpp | 22 +++++++++-- node/Node.hpp | 10 +++++ node/RuntimeEnvironment.hpp | 3 -- node/Switch.cpp | 5 +-- 6 files changed, 32 insertions(+), 104 deletions(-) delete mode 100644 node/CMWC4096.hpp (limited to 'node') diff --git a/node/CMWC4096.hpp b/node/CMWC4096.hpp deleted file mode 100644 index b62d7d67..00000000 --- a/node/CMWC4096.hpp +++ /dev/null @@ -1,91 +0,0 @@ -/* - * ZeroTier One - Network Virtualization Everywhere - * Copyright (C) 2011-2015 ZeroTier, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * -- - * - * ZeroTier may be used and distributed under the terms of the GPLv3, which - * are available at: http://www.gnu.org/licenses/gpl-3.0.html - * - * If you would like to embed ZeroTier into a commercial application or - * redistribute it in a modified binary form, please contact ZeroTier Networks - * LLC. Start here: http://www.zerotier.com/ - */ - -#ifndef ZT_CMWC4096_HPP -#define ZT_CMWC4096_HPP - -#include -#include "Utils.hpp" - -namespace ZeroTier { - -/** - * Complement Multiply With Carry random number generator - * - * Based on original code posted to Usenet in the public domain by - * George Marsaglia. Period is approximately 2^131086. - * - * This is not used for cryptographic purposes but for a very fast - * and high-quality PRNG elsewhere in the code. - */ -class CMWC4096 -{ -public: - /** - * Construct and initialize from secure random source - */ - CMWC4096() - throw() - { - Utils::getSecureRandom(Q,sizeof(Q)); - Utils::getSecureRandom(&c,sizeof(c)); - c %= 809430660; - i = 4095; - } - - inline uint32_t next32() - throw() - { - uint32_t __i = ++i & 4095; - const uint64_t t = (18782ULL * (uint64_t)Q[__i]) + (uint64_t)c; - c = (uint32_t)(t >> 32); - uint32_t x = c + (uint32_t)t; - const uint32_t p = (uint32_t)(x < c); x += p; c += p; - return (Q[__i] = 0xfffffffe - x); - } - - inline uint64_t next64() - throw() - { - return ((((uint64_t)next32()) << 32) ^ (uint64_t)next32()); - } - - inline double nextDouble() - throw() - { - return ((double)(next32()) / 4294967296.0); - } - -private: - uint32_t Q[4096]; - uint32_t c; - uint32_t i; -}; - -} // namespace ZeroTier - -#endif diff --git a/node/Multicaster.cpp b/node/Multicaster.cpp index 77ea2e66..3c105236 100644 --- a/node/Multicaster.cpp +++ b/node/Multicaster.cpp @@ -35,7 +35,6 @@ #include "Switch.hpp" #include "Packet.hpp" #include "Peer.hpp" -#include "CMWC4096.hpp" #include "C25519.hpp" #include "CertificateOfMembership.hpp" @@ -97,7 +96,7 @@ unsigned int Multicaster::gather(const Address &queryingPeer,uint64_t nwid,const // will return different subsets of a large multicast group. k = 0; while ((added < limit)&&(k < gs->second.members.size())&&((appendTo.size() + ZT_ADDRESS_LENGTH) <= ZT_UDP_DEFAULT_PAYLOAD_MTU)) { - rptr = (unsigned int)RR->prng->next32(); + rptr = (unsigned int)RR->node->prng(); restart_member_scan: a = gs->second.members[rptr % (unsigned int)gs->second.members.size()].address.toInt(); @@ -171,7 +170,7 @@ void Multicaster::send( for(unsigned long i=0;i0;--i) { - unsigned long j = RR->prng->next32() % (i + 1); + unsigned long j = (unsigned long)RR->node->prng() % (i + 1); unsigned long tmp = indexes[j]; indexes[j] = indexes[i]; indexes[i] = tmp; diff --git a/node/Node.cpp b/node/Node.cpp index d8bd8910..3df34aec 100644 --- a/node/Node.cpp +++ b/node/Node.cpp @@ -37,7 +37,6 @@ #include "Node.hpp" #include "RuntimeEnvironment.hpp" #include "NetworkController.hpp" -#include "CMWC4096.hpp" #include "Switch.hpp" #include "Multicaster.hpp" #include "AntiRecursion.hpp" @@ -76,6 +75,7 @@ Node::Node( _eventCallback(eventCallback), _networks(), _networks_m(), + _prngStreamPtr(0), _now(now), _lastPingCheck(0), _lastHousekeepingRun(0) @@ -85,6 +85,15 @@ Node::Node( _newestVersionSeen[2] = ZEROTIER_ONE_VERSION_REVISION; _online = false; + // Use Salsa20 alone as a high-quality non-crypto PRNG + { + char foo[32]; + Utils::getSecureRandom(foo,32); + _prng.init(foo,256,foo,8); + memset(_prngStream,0,sizeof(_prngStream)); + _prng.encrypt(_prngStream,_prngStream,sizeof(_prngStream)); + } + std::string idtmp(dataStoreGet("identity.secret")); if ((!idtmp.length())||(!RR->identity.fromString(idtmp))||(!RR->identity.hasPrivate())) { TRACE("identity.secret not found, generating..."); @@ -103,7 +112,6 @@ Node::Node( } try { - RR->prng = new CMWC4096(); RR->sw = new Switch(RR); RR->mc = new Multicaster(RR); RR->antiRec = new AntiRecursion(); @@ -115,7 +123,6 @@ Node::Node( delete RR->antiRec; delete RR->mc; delete RR->sw; - delete RR->prng; throw; } @@ -146,7 +153,6 @@ Node::~Node() delete RR->antiRec; delete RR->mc; delete RR->sw; - delete RR->prng; } ZT1_ResultCode Node::processWirePacket( @@ -510,6 +516,14 @@ void Node::postTrace(const char *module,unsigned int line,const char *fmt,...) } #endif // ZT_TRACE +uint64_t Node::prng() +{ + unsigned int p = (++_prngStreamPtr % (sizeof(_prngStream) / sizeof(uint64_t))); + if (!p) + _prng.encrypt(_prngStream,_prngStream,sizeof(_prngStream)); + return _prngStream[p]; +} + } // namespace ZeroTier /****************************************************************************/ diff --git a/node/Node.hpp b/node/Node.hpp index fe31576c..579d3a57 100644 --- a/node/Node.hpp +++ b/node/Node.hpp @@ -44,6 +44,7 @@ #include "MAC.hpp" #include "Network.hpp" #include "Path.hpp" +#include "Salsa20.hpp" #undef TRACE #ifdef ZT_TRACE @@ -219,6 +220,11 @@ public: 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(); + private: inline SharedPtr _network(uint64_t nwid) const { @@ -253,6 +259,10 @@ private: Mutex _backgroundTasksLock; + unsigned int _prngStreamPtr; + Salsa20 _prng; + uint64_t _prngStream[16]; // repeatedly encrypted with _prng to yield a high-quality non-crypto PRNG stream + uint64_t _now; uint64_t _lastPingCheck; uint64_t _lastHousekeepingRun; diff --git a/node/RuntimeEnvironment.hpp b/node/RuntimeEnvironment.hpp index 228040e7..e5d1f446 100644 --- a/node/RuntimeEnvironment.hpp +++ b/node/RuntimeEnvironment.hpp @@ -38,7 +38,6 @@ namespace ZeroTier { class NodeConfig; class Switch; class Topology; -class CMWC4096; class Node; class Multicaster; class AntiRecursion; @@ -55,7 +54,6 @@ public: node(n), identity(), localNetworkController((NetworkController *)0), - prng((CMWC4096 *)0), sw((Switch *)0), mc((Multicaster *)0), antiRec((AntiRecursion *)0), @@ -83,7 +81,6 @@ public: * These are constant and never null after startup unless indicated. */ - CMWC4096 *prng; Switch *sw; Multicaster *mc; AntiRecursion *antiRec; diff --git a/node/Switch.cpp b/node/Switch.cpp index 62fa02cd..4fd5d769 100644 --- a/node/Switch.cpp +++ b/node/Switch.cpp @@ -42,7 +42,6 @@ #include "InetAddress.hpp" #include "Topology.hpp" #include "Peer.hpp" -#include "CMWC4096.hpp" #include "AntiRecursion.hpp" #include "Packet.hpp" @@ -236,7 +235,7 @@ void Switch::onLocalEthernet(const SharedPtr &network,const MAC &from,c while (numBridges < ZT_MAX_BRIDGE_SPAM) { if (ab == nconf->activeBridges().end()) ab = nconf->activeBridges().begin(); - if (((unsigned long)RR->prng->next32() % (unsigned long)nconf->activeBridges().size()) == 0) { + if (((unsigned long)RR->node->prng() % (unsigned long)nconf->activeBridges().size()) == 0) { bridges[numBridges++] = *ab; ++ab; } else ++ab; @@ -327,7 +326,7 @@ bool Switch::unite(const Address &p1,const Address &p2,bool force) * the order we make each attempted NAT-t favor one or the other going * first, meaning if it doesn't succeed the first time it might the second * and so forth. */ - unsigned int alt = RR->prng->next32() & 1; + unsigned int alt = (unsigned int)RR->node->prng() & 1; unsigned int completed = alt + 2; while (alt != completed) { if ((alt & 1) == 0) { -- cgit v1.2.3 From cf6f30963cdf6de47342e6724ebd7f8e8058ad43 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 7 Jul 2015 10:59:59 -0700 Subject: Kill a potential source of type punning BUS errors on Android, and besides that hack probably did not improve performance at all given the short lenghts of things compared with secureEq() --- node/Utils.hpp | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) (limited to 'node') diff --git a/node/Utils.hpp b/node/Utils.hpp index bd567cf5..70918eb5 100644 --- a/node/Utils.hpp +++ b/node/Utils.hpp @@ -60,20 +60,10 @@ public: static inline bool secureEq(const void *a,const void *b,unsigned int len) throw() { - const char *p1 = (const char *)a; - const char *p2 = (const char *)b; - uint64_t diff = 0; - - while (len >= 8) { - diff |= (*((const uint64_t *)p1) ^ *((const uint64_t *)p2)); - p1 += 8; - p2 += 8; - len -= 8; - } - while (len--) - diff |= (uint64_t)(*p1++ ^ *p2++); - - return (diff == 0ULL); + char diff = 0; + for(unsigned int i=0;i(a))[i] ^ (reinterpret_cast(b))[i] ); + return (diff == 0); } /** -- cgit v1.2.3 From 412389ec755528108e0254e75a9cf43fc53e331a Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 7 Jul 2015 11:49:38 -0700 Subject: Implement ERROR_UNWATNED_MULTICAST --- node/IncomingPacket.cpp | 5 ++++- node/Multicaster.cpp | 14 ++++++++++++++ node/Multicaster.hpp | 9 +++++++++ 3 files changed, 27 insertions(+), 1 deletion(-) (limited to 'node') diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index d5b4e9e6..6c3a0932 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -156,7 +156,10 @@ bool IncomingPacket::_doERROR(const RuntimeEnvironment *RR,const SharedPtr } break; case Packet::ERROR_UNWANTED_MULTICAST: { - // TODO: unsubscribe + uint64_t nwid = at(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD); + MulticastGroup mg(MAC(field(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD + 8,6),6),at(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD + 14)); + TRACE("%.16llx: peer %s unsubscrubed from multicast group %s",nwid,peer->address().toString().c_str(),mg.toString().c_str()); + RR->mc->remove(nwid,mg,peer->address()); } break; default: break; diff --git a/node/Multicaster.cpp b/node/Multicaster.cpp index 3c105236..33424e4a 100644 --- a/node/Multicaster.cpp +++ b/node/Multicaster.cpp @@ -61,6 +61,20 @@ void Multicaster::addMultiple(uint64_t now,uint64_t nwid,const MulticastGroup &m } } +void Multicaster::remove(uint64_t nwid,const MulticastGroup &mg,const Address &member) +{ + Mutex::Lock _l(_groups_m); + std::map< std::pair,MulticastGroupStatus >::iterator g(_groups.find(std::pair(nwid,mg))); + if (g != _groups.end()) { + for(std::vector::iterator m(g->second.members.begin());m!=g->second.members.end();++m) { + if (m->address == member) { + g->second.members.erase(m); + break; + } + } + } +} + unsigned int Multicaster::gather(const Address &queryingPeer,uint64_t nwid,const MulticastGroup &mg,Packet &appendTo,unsigned int limit) const { unsigned char *p; diff --git a/node/Multicaster.hpp b/node/Multicaster.hpp index c6c93b1f..0dd199f9 100644 --- a/node/Multicaster.hpp +++ b/node/Multicaster.hpp @@ -106,6 +106,15 @@ public: */ void addMultiple(uint64_t now,uint64_t nwid,const MulticastGroup &mg,const void *addresses,unsigned int count,unsigned int totalKnown); + /** + * Remove a multicast group member (if present) + * + * @param nwid Network ID + * @param mg Multicast group + * @param member Member to unsubscribe + */ + void remove(uint64_t nwid,const MulticastGroup &mg,const Address &member); + /** * Append gather results to a packet by choosing registered multicast recipients at random * -- cgit v1.2.3