Merge branch 'adamierymenko-dev' into android-jni

This commit is contained in:
Grant Limberg 2015-07-07 19:24:02 -07:00
commit 1ad2cfeedf
32 changed files with 908 additions and 504 deletions

View File

@ -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,36 @@ 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.
*
* 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
*/
int 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
*

View File

@ -1,5 +1,5 @@
CC?=cc
CXX?=c++
CC=cc
CXX=c++
INCLUDES=
DEFS=

View File

@ -22,7 +22,7 @@ CC?=$(shell if [ -e /usr/bin/clang ]; then echo clang; else echo gcc; fi)
CXX?=$(shell if [ -e /usr/bin/clang++ ]; then echo clang++; else echo g++; fi)
INCLUDES=
DEFS=
LIBS=
LDLIBS?=
include objects.mk
OBJS+=osdep/LinuxEthernetTap.o
@ -35,7 +35,7 @@ endif
# Build with ZT_ENABLE_NETWORK_CONTROLLER=1 to build with the Sqlite network controller
ifeq ($(ZT_ENABLE_NETWORK_CONTROLLER),1)
DEFS+=-DZT_ENABLE_NETWORK_CONTROLLER
LIBS+=-L/usr/local/lib -lsqlite3
LDLIBS+=-L/usr/local/lib -lsqlite3
OBJS+=controller/SqliteNetworkController.o
endif
@ -67,13 +67,13 @@ endif
all: one
one: $(OBJS) one.o
$(CXX) $(CXXFLAGS) $(LDFLAGS) -o zerotier-one $(OBJS) one.o $(LIBS)
$(CXX) $(CXXFLAGS) $(LDFLAGS) -o zerotier-one $(OBJS) one.o $(LDLIBS)
$(STRIP) zerotier-one
ln -sf zerotier-one zerotier-idtool
ln -sf zerotier-one zerotier-cli
selftest: $(OBJS) selftest.o
$(CXX) $(CXXFLAGS) $(LDFLAGS) -o zerotier-selftest selftest.o $(OBJS) $(LIBS)
$(CXX) $(CXXFLAGS) $(LDFLAGS) -o zerotier-selftest selftest.o $(OBJS) $(LDLIBS)
$(STRIP) zerotier-selftest
installer: one FORCE

View File

@ -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

View File

@ -304,21 +304,16 @@
*/
#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)
*/
#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
*

View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -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
*

View File

@ -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)

View File

@ -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.

View File

@ -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;

View File

@ -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;
};

View File

@ -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);

View File

@ -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)";
}

View File

@ -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
};
/**

View File

@ -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:
// Must be the same values as ZT1_LocalInterfaceAddressTrust in ZeroTierOne.h
enum Trust
{
TRUST_NORMAL = 0,
TRUST_PRIVACY = 1,
TRUST_ULTIMATE = 2
};
Path() :
_addr(),
_lastSend(0),
_lastReceived(0),
_fixed(false) {}
_metric(0),
_trust(TRUST_NORMAL),
_reliable(false)
{
}
Path(const Path &p) throw() { memcpy(this,&p,sizeof(Path)); }
Path(const InetAddress &addr,bool fixed) :
Path(const InetAddress &addr,int metric,Trust trust,bool reliable) :
_addr(addr),
_lastSend(0),
_lastReceived(0),
_fixed(fixed) {}
inline void init(const InetAddress &addr,bool fixed)
_metric(metric),
_trust(trust),
_reliable(reliable)
{
_addr = addr;
_lastSend = 0;
_lastReceived = 0;
_fixed = fixed;
}
inline Path &operator=(const Path &p)
{
if (this != &p)
memcpy(this,&p,sizeof(Path));
return *this;
}
/**
* @return Physical address
*/
inline const InetAddress &address() const throw() { return _addr; }
inline uint64_t lastSend() const throw() { return _lastSend; }
inline uint64_t lastReceived() const throw() { return _lastReceived; }
/**
* @return Metric (higher == worse) or negative if path is blacklisted
*/
inline int metric() const throw() { return _metric; }
/**
* Called when a packet is sent to this path
*
* This is called automatically by Path::send().
*
* @param t Time of send
* @return Path trust level
*/
inline void sent(uint64_t t)
throw()
{
_lastSend = t;
}
inline Trust trust() const throw() { return _trust; }
/**
* Called when a packet is received from this path
*
* @param t Time of receive
* @return True if path is considered reliable (no NAT keepalives etc. are needed)
*/
inline void received(uint64_t t)
throw()
{
_lastReceived = t;
}
inline bool reliable() const throw() { return _reliable; }
/**
* @return Is this a fixed path?
* @return True if address is non-NULL
*/
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); }
// 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

View File

@ -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++]);

View File

@ -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;
}
/**
@ -182,12 +178,22 @@ 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;

142
node/RemotePath.hpp Normal file
View File

@ -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

View File

@ -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;

View File

@ -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)) {

View File

@ -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);
if (!viaPath) {
SharedPtr<Peer> relay;
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?
}
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);
}
}
RemotePath *viaPath = peer->getBestPath(now);
SharedPtr<Peer> relay;
if (!viaPath) {
// 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 {

View File

@ -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

View File

@ -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);
}

View File

@ -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);
}
/**

View File

@ -692,17 +692,20 @@ void WindowsEthernetTap::threadMain()
ULONGLONG tc = GetTickCount64();
if ((tc - timeOfLastBorkCheck) >= 2500) {
timeOfLastBorkCheck = tc;
MIB_IF_TABLE2 *ift = NULL;
if ((GetIfTable2(&ift) == NO_ERROR)&&(ift)) {
char aabuf[16384];
ULONG aalen = sizeof(aabuf);
if (GetAdaptersAddresses(AF_UNSPEC,GAA_FLAG_SKIP_UNICAST|GAA_FLAG_SKIP_ANYCAST|GAA_FLAG_SKIP_MULTICAST|GAA_FLAG_SKIP_DNS_SERVER|GAA_FLAG_SKIP_FRIENDLY_NAME,(void *)0,reinterpret_cast<PIP_ADAPTER_ADDRESSES>(aabuf),&aalen) == NO_ERROR) {
bool isBorked = false;
for(ULONG r=0;r<ift->NumEntries;++r) {
if (ift->Table[r].InterfaceLuid.Value == _deviceLuid.Value) {
if ((ift->Table[r].InterfaceAndOperStatusFlags.NotMediaConnected)||(ift->Table[r].MediaConnectState == MediaConnectStateDisconnected))
isBorked = true;
PIP_ADAPTER_ADDRESSES aa = reinterpret_cast<PIP_ADAPTER_ADDRESSES>(aabuf);
while (aa) {
if (_deviceLuid.Value == aa->Luid.Value) {
isBorked = (aa->OperStatus != IfOperStatusUp);
break;
}
aa = aa->Next;
}
FreeMibTable(ift);
if (isBorked) {
// Close and reopen tap device if there's an issue (outer loop)
break;

View File

@ -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);

View File

@ -75,11 +75,17 @@ class SqliteNetworkController;
#endif // ZT_ENABLE_NETWORK_CONTROLLER
#ifdef __WINDOWS__
#include <WinSock2.h>
#include <Windows.h>
#include <ShlObj.h>
#include <netioapi.h>
#include <iphlpapi.h>
#else
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <unistd.h>
#include <ifaddrs.h>
#endif
// Include the right tap device driver for this platform -- add new platforms here
@ -123,6 +129,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 +414,7 @@ public:
_nextBackgroundTaskDeadline(0),
_tcpFallbackTunnel((TcpConnection *)0),
_termReason(ONE_STILL_RUNNING),
_port(port),
_run(true)
{
struct sockaddr_in in4;
@ -501,6 +511,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 +565,77 @@ public:
}
}
if ((now - lastLocalInterfaceAddressCheck) >= ZT1_LOCAL_INTERFACE_CHECK_INTERVAL) {
lastLocalInterfaceAddressCheck = now;
#ifdef __UNIX_LIKE__
std::vector<std::string> 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<std::string>::const_iterator d(ztDevices.begin());d!=ztDevices.end();++d) {
if (*d == ifa->ifa_name) {
isZT = true;
break;
}
}
if (!isZT) {
InetAddress ip(ifa->ifa_addr);
ip.setPort(_port);
_node->addLocalInterfaceAddress(reinterpret_cast<const struct sockaddr_storage *>(&ip),0,ZT1_LOCAL_INTERFACE_ADDRESS_TRUST_NORMAL,0);
}
}
ifa = ifa->ifa_next;
}
freeifaddrs(ifatbl);
}
#endif // __UNIX_LIKE__
#ifdef __WINDOWS__
std::vector<NET_LUID> 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->luid());
}
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<PIP_ADAPTER_ADDRESSES>(aabuf),&aalen) == NO_ERROR) {
PIP_ADAPTER_ADDRESSES a = reinterpret_cast<PIP_ADAPTER_ADDRESSES>(aabuf);
while (a) {
bool isZT = false;
for(std::vector<NET_LUID>::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);
ip.setPort(_port);
_node->addLocalInterfaceAddress(reinterpret_cast<const struct sockaddr_storage *>(&ip),0,ZT1_LOCAL_INTERFACE_ADDRESS_TRUST_NORMAL,0);
ua = ua->Next;
}
}
a = a->Next;
}
}
#endif // __WINDOWS__
}
const unsigned long delay = (dl > now) ? (unsigned long)(dl - now) : 100;
clockShouldBe = now + (uint64_t)delay;
_phy.poll(delay);
@ -1158,6 +1240,8 @@ private:
std::string _fatalErrorMessage;
Mutex _termReason_m;
unsigned int _port;
bool _run;
Mutex _run_m;
};

View File

@ -94,6 +94,7 @@
<ClInclude Include="..\..\node\Path.hpp" />
<ClInclude Include="..\..\node\Peer.hpp" />
<ClInclude Include="..\..\node\Poly1305.hpp" />
<ClInclude Include="..\..\node\RemotePath.hpp" />
<ClInclude Include="..\..\node\RuntimeEnvironment.hpp" />
<ClInclude Include="..\..\node\Salsa20.hpp" />
<ClInclude Include="..\..\node\SelfAwareness.hpp" />

View File

@ -344,6 +344,9 @@
<ClInclude Include="..\..\osdep\BackgroundResolver.hpp">
<Filter>Header Files\osdep</Filter>
</ClInclude>
<ClInclude Include="..\..\node\RemotePath.hpp">
<Filter>Header Files\node</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="ZeroTierOne.rc">