summaryrefslogtreecommitdiff
path: root/node
diff options
context:
space:
mode:
Diffstat (limited to 'node')
-rw-r--r--node/CMWC4096.hpp91
-rw-r--r--node/Constants.hpp13
-rw-r--r--node/IncomingPacket.cpp94
-rw-r--r--node/IncomingPacket.hpp1
-rw-r--r--node/InetAddress.cpp8
-rw-r--r--node/Multicaster.cpp19
-rw-r--r--node/Multicaster.hpp9
-rw-r--r--node/Network.cpp56
-rw-r--r--node/Network.hpp6
-rw-r--r--node/Node.cpp78
-rw-r--r--node/Node.hpp26
-rw-r--r--node/OutboundMulticast.cpp3
-rw-r--r--node/Packet.cpp2
-rw-r--r--node/Packet.hpp169
-rw-r--r--node/Path.hpp172
-rw-r--r--node/Peer.cpp77
-rw-r--r--node/Peer.hpp37
-rw-r--r--node/RemotePath.hpp142
-rw-r--r--node/RuntimeEnvironment.hpp3
-rw-r--r--node/SelfAwareness.cpp2
-rw-r--r--node/Switch.cpp185
-rw-r--r--node/Switch.hpp15
-rw-r--r--node/Topology.cpp2
-rw-r--r--node/Utils.hpp18
24 files changed, 742 insertions, 486 deletions
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 <http://www.gnu.org/licenses/>.
- *
- * --
- *
- * 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 <stdint.h>
-#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/Constants.hpp b/node/Constants.hpp
index ac9dbc99..d15fef13 100644
--- a/node/Constants.hpp
+++ b/node/Constants.hpp
@@ -305,19 +305,14 @@
#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
+ * Minimum delay between attempts to confirm new paths to peers (to avoid HELLO flooding)
*/
-#define ZT_MIN_BEACON_RESPONSE_INTERVAL 2500
+#define ZT_MIN_PATH_CONFIRMATION_INTERVAL 5000
/**
- * Minimum delay between attempts to confirm new paths to peers (to avoid HELLO flooding)
+ * Interval between direct path pushes in milliseconds
*/
-#define ZT_MIN_PATH_CONFIRMATION_INTERVAL 5000
+#define ZT_DIRECT_PATH_PUSH_INTERVAL 300000
/**
* Sanity limit on maximum bridge routes
diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp
index 7e2bcdaa..6c3a0932 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());
@@ -133,6 +134,9 @@ bool IncomingPacket::_doERROR(const RuntimeEnvironment *RR,const SharedPtr<Peer>
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> network(RR->node->network(at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD)));
if (network) {
SharedPtr<NetworkConfig> nconf(network->config2());
@@ -152,7 +156,10 @@ bool IncomingPacket::_doERROR(const RuntimeEnvironment *RR,const SharedPtr<Peer>
} break;
case Packet::ERROR_UNWANTED_MULTICAST: {
- // TODO: unsubscribe
+ uint64_t nwid = at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD);
+ MulticastGroup mg(MAC(field(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD + 8,6),6),at<uint32_t>(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;
@@ -169,8 +176,20 @@ bool IncomingPacket::_doERROR(const RuntimeEnvironment *RR,const SharedPtr<Peer>
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<uint16_t>(ZT_PROTO_VERB_HELLO_IDX_REVISION);
@@ -178,6 +197,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
@@ -192,16 +215,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> peer(RR->topology->getPeer(id.address()));
if (peer) {
// We already have an identity with this address -- check for collisions
@@ -244,12 +257,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<Peer> newPeer(new Peer(RR->identity,id));
if (!dearmor(newPeer->key())) {
RR->node->postEvent(ZT1_EVENT_AUTHENTICATION_FAILURE,(const void *)&_remoteAddress);
@@ -428,7 +443,7 @@ bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,const SharedPtr<Peer> &p
offset += com.deserialize(*this,ZT_PROTO_VERB_MULTICAST_FRAME__OK__IDX_COM_AND_GATHER_RESULTS);
SharedPtr<Network> network(RR->node->network(nwid));
if ((network)&&(com.hasRequiredFields()))
- network->addMembershipCertificate(com,false);
+ network->validateAndAddMembershipCertificate(com);
}
if ((flags & 0x02) != 0) {
@@ -553,14 +568,17 @@ bool IncomingPacket::_doEXT_FRAME(const RuntimeEnvironment *RR,const SharedPtr<P
const unsigned int flags = (*this)[ZT_PROTO_VERB_EXT_FRAME_IDX_FLAGS];
unsigned int comLen = 0;
+ bool comFailed = false;
if ((flags & 0x01) != 0) {
CertificateOfMembership com;
comLen = com.deserialize(*this,ZT_PROTO_VERB_EXT_FRAME_IDX_COM);
- if (com.hasRequiredFields())
- network->addMembershipCertificate(com,false);
+ 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;
@@ -595,9 +613,7 @@ bool IncomingPacket::_doEXT_FRAME(const RuntimeEnvironment *RR,const SharedPtr<P
TRACE("dropped EXT_FRAME from %s@%s(%s) to %s: sender not allowed to bridge into %.16llx",from.toString().c_str(),peer->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;
@@ -649,7 +665,7 @@ bool IncomingPacket::_doNETWORK_MEMBERSHIP_CERTIFICATE(const RuntimeEnvironment
if (com.hasRequiredFields()) {
SharedPtr<Network> network(RR->node->network(com.networkId()));
if (network)
- network->addMembershipCertificate(com,false);
+ network->validateAndAddMembershipCertificate(com);
}
}
@@ -807,7 +823,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
@@ -884,6 +900,44 @@ bool IncomingPacket::_doMULTICAST_FRAME(const RuntimeEnvironment *RR,const Share
return true;
}
+bool IncomingPacket::_doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer)
+{
+ try {
+ unsigned int count = at<uint16_t>(ZT_PACKET_IDX_PAYLOAD);
+ 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<uint16_t>(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<uint16_t>(ptr + 4));
+ 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<uint16_t>(ptr + 16));
+ if ( ((flags & (0x01 | 0x02)) == 0) && (Path::isAddressValidForPath(a)) )
+ 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;
+}
+
void IncomingPacket::_sendErrorNeedCertificate(const RuntimeEnvironment *RR,const SharedPtr<Peer> &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> &peer);
bool _doMULTICAST_GATHER(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
bool _doMULTICAST_FRAME(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
+ bool _doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,const SharedPtr<Peer> &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> &peer,uint64_t nwid);
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<const unsigned char *>(reinterpret_cast<const struct sockaddr_in6 *>(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/Multicaster.cpp b/node/Multicaster.cpp
index 77ea2e66..33424e4a 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"
@@ -62,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<uint64_t,MulticastGroup>,MulticastGroupStatus >::iterator g(_groups.find(std::pair<uint64_t,MulticastGroup>(nwid,mg)));
+ if (g != _groups.end()) {
+ for(std::vector<MulticastGroupMember>::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;
@@ -97,7 +110,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 +184,7 @@ void Multicaster::send(
for(unsigned long i=0;i<gs.members.size();++i)
indexes[i] = i;
for(unsigned long i=(unsigned long)gs.members.size()-1;i>0;--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/Multicaster.hpp b/node/Multicaster.hpp
index c6c93b1f..0dd199f9 100644
--- a/node/Multicaster.hpp
+++ b/node/Multicaster.hpp
@@ -107,6 +107,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
*
* This appends the following fields to the packet:
diff --git a/node/Network.cpp b/node/Network.cpp
index 4414e4d1..adc8e1b8 100644
--- a/node/Network.cpp
+++ b/node/Network.cpp
@@ -264,56 +264,58 @@ 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);
}
-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<Peer> signer(RR->topology->getPeer(cert.signedBy()));
+ SharedPtr<Peer> 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..d7320d46 100644
--- a/node/Network.hpp
+++ b/node/Network.hpp
@@ -179,12 +179,12 @@ 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
+ * 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/Node.cpp b/node/Node.cpp
index 8cdc6d62..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,16 +75,25 @@ Node::Node(
_eventCallback(eventCallback),
_networks(),
_networks_m(),
+ _prngStreamPtr(0),
_now(now),
_lastPingCheck(0),
- _lastHousekeepingRun(0),
- _lastBeacon(0)
+ _lastHousekeepingRun(0)
{
_newestVersionSeen[0] = ZEROTIER_ONE_VERSION_MAJOR;
_newestVersionSeen[1] = ZEROTIER_ONE_VERSION_MINOR;
_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...");
@@ -104,7 +112,6 @@ Node::Node(
}
try {
- RR->prng = new CMWC4096();
RR->sw = new Switch(RR);
RR->mc = new Multicaster(RR);
RR->antiRec = new AntiRecursion();
@@ -116,7 +123,6 @@ Node::Node(
delete RR->antiRec;
delete RR->mc;
delete RR->sw;
- delete RR->prng;
throw;
}
@@ -147,7 +153,6 @@ Node::~Node()
delete RR->antiRec;
delete RR->mc;
delete RR->sw;
- delete RR->prng;
}
ZT1_ResultCode Node::processWirePacket(
@@ -269,19 +274,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<uint32_t *>(p)) = RR->prng->next32();
- p = beacon + 4;
- *(reinterpret_cast<uint32_t *>(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;
}
@@ -388,10 +380,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<Path> paths(pi->second->paths());
- Path *bestPath = pi->second->getBestPath(_now);
+ std::vector<RemotePath> paths(pi->second->paths());
+ RemotePath *bestPath = pi->second->getBestPath(_now);
p->pathCount = 0;
- for(std::vector<Path>::iterator path(paths.begin());path!=paths.end();++path) {
+ for(std::vector<RemotePath>::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();
@@ -440,6 +432,24 @@ void Node::freeQueryResult(void *qr)
::free(qr);
}
+int Node::addLocalInterfaceAddress(const struct sockaddr_storage *addr,int metric,ZT1_LocalInterfaceAddressTrust trust,int reliable)
+{
+ if (Path::isAddressValidForPath(*(reinterpret_cast<const InetAddress *>(addr)))) {
+ Mutex::Lock _l(_directPaths_m);
+ _directPaths.push_back(Path(*(reinterpret_cast<const InetAddress *>(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()
+{
+ Mutex::Lock _l(_directPaths_m);
+ _directPaths.clear();
+}
+
void Node::setNetconfMaster(void *networkControllerInstance)
{
RR->localNetworkController = reinterpret_cast<NetworkController *>(networkControllerInstance);
@@ -506,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
/****************************************************************************/
@@ -693,6 +711,22 @@ void ZT1_Node_setNetconfMaster(ZT1_Node *node,void *networkControllerInstance)
} catch ( ... ) {}
}
+int ZT1_Node_addLocalInterfaceAddress(ZT1_Node *node,const struct sockaddr_storage *addr,int metric,ZT1_LocalInterfaceAddressTrust trust,int reliable)
+{
+ try {
+ return reinterpret_cast<ZeroTier::Node *>(node)->addLocalInterfaceAddress(addr,metric,trust,reliable);
+ } catch ( ... ) {
+ return 0;
+ }
+}
+
+void ZT1_Node_clearLocalInterfaceAddresses(ZT1_Node *node)
+{
+ try {
+ reinterpret_cast<ZeroTier::Node *>(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 2d2898b5..579d3a57 100644
--- a/node/Node.hpp
+++ b/node/Node.hpp
@@ -43,6 +43,8 @@
#include "Mutex.hpp"
#include "MAC.hpp"
#include "Network.hpp"
+#include "Path.hpp"
+#include "Salsa20.hpp"
#undef TRACE
#ifdef ZT_TRACE
@@ -103,6 +105,8 @@ public:
ZT1_VirtualNetworkConfig *networkConfig(uint64_t nwid) const;
ZT1_VirtualNetworkList *networks() const;
void freeQueryResult(void *qr);
+ int addLocalInterfaceAddress(const struct sockaddr_storage *addr,int metric,ZT1_LocalInterfaceAddressTrust trust,int reliable);
+ void clearLocalInterfaceAddresses();
void setNetconfMaster(void *networkControllerInstance);
// Internal functions ------------------------------------------------------
@@ -171,6 +175,15 @@ public:
return nw;
}
+ /**
+ * @return Potential direct paths to me a.k.a. local interface addresses
+ */
+ inline std::vector<Path> 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<ZT1_Node *>(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<ZT1_Node *>(this),_uPtr,name,(const void *)0,0,0); }
@@ -207,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> _network(uint64_t nwid) const
{
@@ -236,12 +254,18 @@ private:
std::vector< std::pair< uint64_t, SharedPtr<Network> > > _networks;
Mutex _networks_m;
+ std::vector<Path> _directPaths;
+ Mutex _directPaths_m;
+
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;
- uint64_t _lastBeacon;
unsigned int _newestVersionSeen[3]; // major, minor, revision
bool _online;
};
diff --git a/node/OutboundMulticast.cpp b/node/OutboundMulticast.cpp
index f62046be..46116c07 100644
--- a/node/OutboundMulticast.cpp
+++ b/node/OutboundMulticast.cpp
@@ -104,7 +104,7 @@ void OutboundMulticast::sendOnly(const RuntimeEnvironment *RR,const Address &toA
{
if (_haveCom) {
SharedPtr<Network> network(RR->node->network(_nwid));
- if (network->peerNeedsOurMembershipCertificate(toAddr,RR->node->now())) {
+ 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());
@@ -112,6 +112,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);
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..e84306c2 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
@@ -174,24 +233,17 @@
*/
#define ZT_PROTO_MIN_FRAGMENT_LENGTH ZT_PACKET_FRAGMENT_IDX_PAYLOAD
-/**
- * Length of LAN beacon packets
- */
-#define ZT_PROTO_BEACON_LENGTH 13
-
-/**
- * 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
@@ -326,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<ZT_PROTO_MAX_PACKET_LENGTH>
{
@@ -636,7 +678,8 @@ public:
* <[...] serialized certificate of membership>
* [ ... additional certificates may follow ...]
*
- * Certificate contains network ID, peer it was issued for, etc.
+ * 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.
*/
@@ -680,10 +723,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 +810,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 +867,51 @@ public:
VERB_SET_EPHEMERAL_KEY = 15,
/* Push of potential endpoints for direct communication:
+ * <[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)>
+ * <[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:
+ * 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.
*
* 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. 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_PHYSICAL_ADDRESS_PUSH = 16
+ VERB_PUSH_DIRECT_PATHS = 16
};
/**
diff --git a/node/Path.hpp b/node/Path.hpp
index 393b7225..cd21444b 100644
--- a/node/Path.hpp
+++ b/node/Path.hpp
@@ -28,144 +28,65 @@
#ifndef ZT_PATH_HPP
#define ZT_PATH_HPP
-#include <stdint.h>
-#include <string.h>
-
-#include <stdexcept>
-#include <string>
-#include <algorithm>
-
#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)
+ // Must be the same values as ZT1_LocalInterfaceAddressTrust in ZeroTierOne.h
+ enum Trust
{
- 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; }
+ TRUST_NORMAL = 0,
+ TRUST_PRIVACY = 1,
+ TRUST_ULTIMATE = 2
+ };
- /**
- * 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()
+ Path() :
+ _addr(),
+ _metric(0),
+ _trust(TRUST_NORMAL),
+ _reliable(false)
{
- _lastSend = t;
}
- /**
- * Called when a packet is received from this path
- *
- * @param t Time of receive
- */
- inline void received(uint64_t t)
- throw()
+ Path(const InetAddress &addr,int metric,Trust trust,bool reliable) :
+ _addr(addr),
+ _metric(metric),
+ _trust(trust),
+ _reliable(reliable)
{
- _lastReceived = t;
}
/**
- * @return Is this a fixed path?
+ * @return Physical address
*/
- inline bool fixed() const throw() { return _fixed; }
+ inline const InetAddress &address() const throw() { return _addr; }
/**
- * @param f New value of fixed path flag
+ * @return Metric (higher == worse) or negative if path is blacklisted
*/
- inline void setFixed(bool f) throw() { _fixed = f; }
+ inline int metric() const throw() { return _metric; }
/**
- * @param now Current time
- * @return True if this path is fixed or has received data in last ACTIVITY_TIMEOUT ms
+ * @return Path trust level
*/
- inline bool active(uint64_t now) const
- throw()
- {
- return ( (_fixed) || ((now - _lastReceived) < ZT_PEER_ACTIVITY_TIMEOUT) );
- }
+ inline Trust trust() const throw() { return _trust; }
/**
- * 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
+ * @return True if path is considered reliable (no NAT keepalives etc. are needed)
*/
- 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;
- }
+ inline bool reliable() const throw() { return _reliable; }
/**
- * @param now Current time
- * @return Human-readable address and other information about this path
+ * @return True if address is non-NULL
*/
- 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); }
+ // 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); }
@@ -173,11 +94,44 @@ 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); }
-private:
+ /**
+ * 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;
- uint64_t _lastSend;
- uint64_t _lastReceived;
- bool _fixed;
+ int _metric; // negative == blacklisted
+ Trust _trust;
+ bool _reliable;
};
} // namespace ZeroTier
diff --git a/node/Peer.cpp b/node/Peer.cpp
index 96caa72c..84aa8bef 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,73 @@ void Peer::doPingAndKeepalive(const RuntimeEnvironment *RR,uint64_t now)
}
}
-void Peer::addPath(const Path &newp)
+void Peer::pushDirectPaths(const RuntimeEnvironment *RR,RemotePath *path,uint64_t now,bool force)
+{
+ if ((((now - _lastDirectPathPush) >= ZT_DIRECT_PATH_PUSH_INTERVAL)||(force))) {
+ _lastDirectPathPush = now;
+
+ std::vector<Path> 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<Path>::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((uint8_t)((addressType == 4) ? 6 : 18));
+ 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);
+ outp.armor(_key,true);
+ path->send(RR,outp.data(),outp.size(),now);
+ }
+ }
+ }
+}
+
+void Peer::addPath(const RemotePath &newp)
{
unsigned int np = _numPaths;
@@ -218,7 +285,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..f5118794 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<np;++p) {
if ((_paths[p].active(now))&&(_paths[p].lastReceived() >= 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;
}
/**
@@ -183,11 +179,21 @@ public:
void doPingAndKeepalive(const RuntimeEnvironment *RR,uint64_t now);
/**
+ * Push direct paths if we haven't done so in [rate limit] milliseconds
+ *
+ * @param RR Runtime environment
+ * @param path Remote path to use to send the push
+ * @param now Current time
+ * @param force If true, push regardless of rate limit
+ */
+ void pushDirectPaths(const RuntimeEnvironment *RR,RemotePath *path,uint64_t now,bool force);
+
+ /**
* @return All known direct paths to this peer
*/
- inline std::vector<Path> paths() const
+ inline std::vector<RemotePath> paths() const
{
- std::vector<Path> pp;
+ std::vector<RemotePath> pp;
for(unsigned int p=0,np=_numPaths;p<np;++p)
pp.push_back(_paths[p]);
return pp;
@@ -295,7 +301,7 @@ public:
*
* @param p New path to add
*/
- void addPath(const Path &newp);
+ void addPath(const RemotePath &newp);
/**
* Clear paths
@@ -412,12 +418,13 @@ private:
uint64_t _lastMulticastFrame;
uint64_t _lastAnnouncedTo;
uint64_t _lastPathConfirmationSent;
+ uint64_t _lastDirectPathPush;
uint16_t _vProto;
uint16_t _vMajor;
uint16_t _vMinor;
uint16_t _vRevision;
Identity _id;
- Path _paths[ZT1_MAX_PEER_NETWORK_PATHS];
+ RemotePath _paths[ZT1_MAX_PEER_NETWORK_PATHS];
unsigned int _numPaths;
unsigned int _latency;
diff --git a/node/RemotePath.hpp b/node/RemotePath.hpp
new file mode 100644
index 00000000..5592c8e1
--- /dev/null
+++ b/node/RemotePath.hpp
@@ -0,0 +1,142 @@
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * 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_REMOTEPATH_HPP
+#define ZT_REMOTEPATH_HPP
+
+#include <stdint.h>
+#include <string.h>
+
+#include <stdexcept>
+#include <algorithm>
+
+#include "Path.hpp"
+#include "Node.hpp"
+#include "AntiRecursion.hpp"
+#include "RuntimeEnvironment.hpp"
+
+namespace ZeroTier {
+
+/**
+ * Path to a remote peer
+ *
+ * This extends Path to include status information about path activity.
+ */
+class RemotePath : public Path
+{
+public:
+ RemotePath() :
+ Path(),
+ _lastSend(0),
+ _lastReceived(0),
+ _fixed(false) {}
+
+ RemotePath(const InetAddress &addr,bool fixed) :
+ Path(addr,0,TRUST_NORMAL,false),
+ _lastSend(0),
+ _lastReceived(0),
+ _fixed(fixed) {}
+
+ inline uint64_t lastSend() const throw() { return _lastSend; }
+ inline uint64_t lastReceived() const throw() { return _lastReceived; }
+
+ /**
+ * @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()
+ {
+ _fixed = f;
+ }
+
+ /**
+ * Called when a packet is sent to this remote path
+ *
+ * This is called automatically by RemotePath::send().
+ *
+ * @param t Time of send
+ */
+ inline void sent(uint64_t t)
+ throw()
+ {
+ _lastSend = t;
+ }
+
+ /**
+ * Called when a packet is received from this remote path
+ *
+ * @param t Time of receive
+ */
+ inline void received(uint64_t t)
+ throw()
+ {
+ _lastReceived = t;
+ }
+
+ /**
+ * @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;
+ }
+
+private:
+ uint64_t _lastSend;
+ uint64_t _lastReceived;
+ bool _fixed;
+};
+
+} // namespace ZeroTier
+
+#endif
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/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<Peer> sn(RR->topology->getBestRoot());
if (sn) {
- Path *snp = sn->getBestPath(now);
+ RemotePath *snp = sn->getBestPath(now);
if (snp) {
for(std::vector< SharedPtr<Peer> >::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 236c1e66..4fd5d769 100644
--- a/node/Switch.cpp
+++ b/node/Switch.cpp
@@ -42,15 +42,30 @@
#include "InetAddress.hpp"
#include "Topology.hpp"
#include "Peer.hpp"
-#include "CMWC4096.hpp"
#include "AntiRecursion.hpp"
#include "Packet.hpp"
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),
- _lastBeacon(0)
+ RR(renv)
{
}
@@ -61,9 +76,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<ZT_PROTO_BEACON_LENGTH>(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) {
@@ -165,42 +178,34 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &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)) {
- 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);
+ 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);
- send(outp,true,network->id());
- }
-
- if (fromBridged) {
- // EXT_FRAME is used for bridging or if we want to include a COM
- Packet outp(toZT,RR->identity.address(),Packet::VERB_EXT_FRAME);
- outp.append(network->id());
- outp.append((unsigned char)0);
- to.appendTo(outp);
- from.appendTo(outp);
- outp.append((uint16_t)etherType);
- outp.append(data,len);
- 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);
- 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",network->id(),from.toString().c_str(),to.toString().c_str(),etherTypeName(etherType),etherType,vlanId,len,(int)fromBridged);
+ 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;
}
@@ -210,22 +215,19 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &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.
+ 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()) {
/* 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<Address>::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
while (ab != nconf->activeBridges().end()) {
- if (network->isAllowed(*ab)) // config sanity check
- bridges[numBridges++] = *ab;
+ bridges[numBridges++] = *ab;
++ab;
}
} else {
@@ -233,9 +235,8 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &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 (network->isAllowed(*ab)) // config sanity check
- bridges[numBridges++] = *ab;
+ if (((unsigned long)RR->node->prng() % (unsigned long)nconf->activeBridges().size()) == 0) {
+ bridges[numBridges++] = *ab;
++ab;
} else ++ab;
}
@@ -245,7 +246,12 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,c
for(unsigned int b=0;b<numBridges;++b) {
Packet outp(bridges[b],RR->identity.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);
@@ -320,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) {
@@ -529,22 +535,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);
@@ -687,23 +677,6 @@ void Switch::_handleRemotePacketHead(const InetAddress &fromAddr,const void *dat
}
}
-void Switch::_handleBeacon(const InetAddress &fromAddr,const Buffer<ZT_PROTO_BEACON_LENGTH> &data)
-{
- Address beaconAddr(data.field(ZT_PROTO_BEACON_IDX_ADDRESS,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH);
- if (beaconAddr == RR->identity.address())
- return;
- SharedPtr<Peer> 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<Peer> root(RR->topology->getBestRoot(peersAlreadyConsulted,numPeersAlreadyConsulted,false));
@@ -724,32 +697,43 @@ 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);
+ SharedPtr<Network> network;
+ SharedPtr<NetworkConfig> nconf;
+ if (nwid) {
+ 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<Peer> relay;
if (!viaPath) {
- SharedPtr<Peer> relay;
-
- if (nwid) {
- SharedPtr<Network> network(RR->node->network(nwid));
- if (network) {
- SharedPtr<NetworkConfig> nconf(network->config2());
- if (nconf) {
- unsigned int latency = ~((unsigned int)0);
- for(std::vector< std::pair<Address,InetAddress> >::const_iterator r(nconf->relays().begin());r!=nconf->relays().end();++r) {
- if (r->first != peer->address()) {
- SharedPtr<Peer> rp(RR->topology->getPeer(r->first));
- if ((rp)&&(rp->hasActiveDirectPath(now))&&(rp->latency() <= latency))
- rp.swap(relay);
- }
- }
+ // See if this network has a preferred relay (if packet has an associated network)
+ if (nconf) {
+ unsigned int latency = ~((unsigned int)0);
+ for(std::vector< std::pair<Address,InetAddress> >::const_iterator r(nconf->relays().begin());r!=nconf->relays().end();++r) {
+ if (r->first != peer->address()) {
+ SharedPtr<Peer> rp(RR->topology->getPeer(r->first));
+ if ((rp)&&(rp->hasActiveDirectPath(now))&&(rp->latency() <= latency))
+ rp.swap(relay);
}
}
}
+ // Otherwise relay off a root server
if (!relay)
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);
@@ -761,7 +745,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));
@@ -777,6 +761,7 @@ bool Switch::_trySend(const Packet &packet,bool encrypt,uint64_t nwid)
remaining -= chunkSize;
}
}
+
return true;
}
} else {
diff --git a/node/Switch.hpp b/node/Switch.hpp
index 0ba4c138..89c9a56b 100644
--- a/node/Switch.hpp
+++ b/node/Switch.hpp
@@ -108,10 +108,14 @@ 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. 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)
- * @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);
@@ -173,22 +177,13 @@ 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);
- void _handleBeacon(const InetAddress &fromAddr,const Buffer<ZT_PROTO_BEACON_LENGTH> &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
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<InetAddress>
if (!p)
p = SharedPtr<Peer>(new Peer(RR->identity,i->first));
for(std::vector<InetAddress>::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);
}
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<len;++i)
+ diff |= ( (reinterpret_cast<const char *>(a))[i] ^ (reinterpret_cast<const char *>(b))[i] );
+ return (diff == 0);
}
/**