mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2025-02-09 12:41:09 +00:00
Merge branch 'adamierymenko-dev' into android-jni
This commit is contained in:
commit
1ad2cfeedf
@ -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
|
||||
*
|
||||
|
@ -1,5 +1,5 @@
|
||||
CC?=cc
|
||||
CXX?=c++
|
||||
CC=cc
|
||||
CXX=c++
|
||||
|
||||
INCLUDES=
|
||||
DEFS=
|
||||
|
@ -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
|
||||
|
@ -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
|
@ -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
|
||||
*
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
*
|
||||
|
@ -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)
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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);
|
||||
|
@ -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)";
|
||||
}
|
||||
|
169
node/Packet.hpp
169
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
|
||||
};
|
||||
|
||||
/**
|
||||
|
180
node/Path.hpp
180
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:
|
||||
// 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
|
||||
|
@ -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++]);
|
||||
|
@ -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
142
node/RemotePath.hpp
Normal 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
|
@ -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;
|
||||
|
@ -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)) {
|
||||
|
185
node/Switch.cpp
185
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);
|
||||
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 {
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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;
|
||||
|
18
selftest.cpp
18
selftest.cpp
@ -193,6 +193,24 @@ static int testCrypto()
|
||||
std::cout << "[crypto] Salsa20 SSE: DISABLED" << std::endl;
|
||||
#endif
|
||||
|
||||
std::cout << "[crypto] Benchmarking Salsa20/8... "; std::cout.flush();
|
||||
{
|
||||
unsigned char *bb = (unsigned char *)::malloc(1234567);
|
||||
for(unsigned int i=0;i<1234567;++i)
|
||||
bb[i] = (unsigned char)i;
|
||||
Salsa20 s20(s20TV0Key,256,s20TV0Iv,8);
|
||||
double bytes = 0.0;
|
||||
uint64_t start = OSUtils::now();
|
||||
for(unsigned int i=0;i<200;++i) {
|
||||
s20.encrypt(bb,bb,1234567);
|
||||
bytes += 1234567.0;
|
||||
}
|
||||
uint64_t end = OSUtils::now();
|
||||
SHA512::hash(buf1,bb,1234567);
|
||||
std::cout << ((bytes / 1048576.0) / ((double)(end - start) / 1000.0)) << " MiB/second (" << Utils::hex(buf1,16) << ')' << std::endl;
|
||||
::free((void *)bb);
|
||||
}
|
||||
|
||||
std::cout << "[crypto] Benchmarking Salsa20/12... "; std::cout.flush();
|
||||
{
|
||||
unsigned char *bb = (unsigned char *)::malloc(1234567);
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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" />
|
||||
|
@ -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">
|
||||
|
Loading…
x
Reference in New Issue
Block a user