diff options
author | Adam Ierymenko <adam.ierymenko@gmail.com> | 2016-11-22 10:54:58 -0800 |
---|---|---|
committer | Adam Ierymenko <adam.ierymenko@gmail.com> | 2016-11-22 10:54:58 -0800 |
commit | 42ba70e79e3f1484f7bdde5832658cbd179649dc (patch) | |
tree | df4f5b0db4ab5d0bc66a2263baa2e4a9b94d1a42 | |
parent | cbaef66e82eeec05dfb005bd34cafc0c1cb411f7 (diff) | |
download | infinitytier-42ba70e79e3f1484f7bdde5832658cbd179649dc.tar.gz infinitytier-42ba70e79e3f1484f7bdde5832658cbd179649dc.zip |
Replace long callback arg list with struct, and implement path whitelisting, path blacklisting, and local.conf support for roles.
-rw-r--r-- | include/ZeroTierOne.h | 97 | ||||
-rw-r--r-- | node/IncomingPacket.cpp | 6 | ||||
-rw-r--r-- | node/Node.cpp | 88 | ||||
-rw-r--r-- | node/Node.hpp | 86 | ||||
-rw-r--r-- | node/Peer.cpp | 2 | ||||
-rw-r--r-- | node/Switch.cpp | 2 | ||||
-rw-r--r-- | service/OneService.cpp | 238 |
7 files changed, 302 insertions, 217 deletions
diff --git a/include/ZeroTierOne.h b/include/ZeroTierOne.h index 399f090c..72da53f2 100644 --- a/include/ZeroTierOne.h +++ b/include/ZeroTierOne.h @@ -1495,8 +1495,9 @@ typedef int (*ZT_WirePacketSendFunction)( * Paramters: * (1) Node * (2) User pointer - * (3) Local interface address - * (4) Remote address + * (3) ZeroTier address or 0 for none/any + * (4) Local interface address + * (5) Remote address * * This function must return nonzero (true) if the path should be used. * @@ -1515,14 +1516,88 @@ typedef int (*ZT_WirePacketSendFunction)( typedef int (*ZT_PathCheckFunction)( ZT_Node *, /* Node */ void *, /* User ptr */ + uint64_t, /* ZeroTier address */ const struct sockaddr_storage *, /* Local address */ const struct sockaddr_storage *); /* Remote address */ +/** + * Function to get physical addresses for ZeroTier peers + * + * Parameters: + * (1) Node + * (2) User pointer + * (3) ZeroTier address (least significant 40 bits) + * (4) Desried address family or -1 for any + * (5) Buffer to fill with result + * + * If provided this function will be occasionally called to get physical + * addresses that might be tried to reach a ZeroTier address. It must + * return a nonzero (true) value if the result buffer has been filled + * with an address. + */ +typedef int (*ZT_PathLookupFunction)( + ZT_Node *, /* Node */ + void *, /* User ptr */ + uint64_t, /* ZeroTier address (40 bits) */ + int, /* Desired ss_family or -1 for any */ + struct sockaddr_storage *); /* Result buffer */ + /****************************************************************************/ /* C Node API */ /****************************************************************************/ /** + * Structure for configuring ZeroTier core callback functions + */ +struct ZT_Node_Callbacks +{ + /** + * Struct version -- must currently be 0 + */ + long version; + + /** + * REQUIRED: Function to get objects from persistent storage + */ + ZT_DataStoreGetFunction dataStoreGetFunction; + + /** + * REQUIRED: Function to store objects in persistent storage + */ + ZT_DataStorePutFunction dataStorePutFunction; + + /** + * REQUIRED: Function to send packets over the physical wire + */ + ZT_WirePacketSendFunction wirePacketSendFunction; + + /** + * REQUIRED: Function to inject frames into a virtual network's TAP + */ + ZT_VirtualNetworkFrameFunction virtualNetworkFrameFunction; + + /** + * REQUIRED: Function to be called when virtual networks are configured or changed + */ + ZT_VirtualNetworkConfigFunction virtualNetworkConfigFunction; + + /** + * REQUIRED: Function to be called to notify external code of important events + */ + ZT_EventCallback eventCallback; + + /** + * OPTIONAL: Function to check whether a given physical path should be used + */ + ZT_PathCheckFunction pathCheckFunction; + + /** + * OPTIONAL: Function to get hints to physical paths to ZeroTier addresses + */ + ZT_PathLookupFunction pathLookupFunction; +}; + +/** * Create a new ZeroTier One node * * Note that this can take a few seconds the first time it's called, as it @@ -1533,25 +1608,11 @@ typedef int (*ZT_PathCheckFunction)( * * @param node Result: pointer is set to new node instance on success * @param uptr User pointer to pass to functions/callbacks + * @param callbacks Callback function configuration * @param now Current clock in milliseconds - * @param dataStoreGetFunction Function called to get objects from persistent storage - * @param dataStorePutFunction Function called to put objects in persistent storage - * @param virtualNetworkConfigFunction Function to be called when virtual LANs are created, deleted, or their config parameters change - * @param pathCheckFunction A function to check whether a path should be used for ZeroTier traffic, or NULL to allow any path - * @param eventCallback Function to receive status updates and non-fatal error notices * @return OK (0) or error code if a fatal error condition has occurred */ -enum ZT_ResultCode ZT_Node_new( - ZT_Node **node, - void *uptr, - uint64_t now, - ZT_DataStoreGetFunction dataStoreGetFunction, - ZT_DataStorePutFunction dataStorePutFunction, - ZT_WirePacketSendFunction wirePacketSendFunction, - ZT_VirtualNetworkFrameFunction virtualNetworkFrameFunction, - ZT_VirtualNetworkConfigFunction virtualNetworkConfigFunction, - ZT_PathCheckFunction pathCheckFunction, - ZT_EventCallback eventCallback); +enum ZT_ResultCode ZT_Node_new(ZT_Node **node,void *uptr,const struct ZT_Node_Callbacks *callbacks,uint64_t now); /** * Delete a node and free all resources it consumes diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index 41f3e47d..7b828f8b 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -552,7 +552,7 @@ bool IncomingPacket::_doRENDEZVOUS(const RuntimeEnvironment *RR,const SharedPtr< const unsigned int addrlen = (*this)[ZT_PROTO_VERB_RENDEZVOUS_IDX_ADDRLEN]; if ((port > 0)&&((addrlen == 4)||(addrlen == 16))) { const InetAddress atAddr(field(ZT_PROTO_VERB_RENDEZVOUS_IDX_ADDRESS,addrlen),addrlen,port); - if (RR->node->shouldUsePathForZeroTierTraffic(_path->localAddress(),atAddr)) { + if (RR->node->shouldUsePathForZeroTierTraffic(with,_path->localAddress(),atAddr)) { RR->node->putPacket(_path->localAddress(),atAddr,"ABRE",4,2); // send low-TTL junk packet to 'open' local NAT(s) and stateful firewalls rendezvousWith->attemptToContactAt(_path->localAddress(),atAddr,RR->node->now()); TRACE("RENDEZVOUS from %s says %s might be at %s, sent verification attempt",peer->address().toString().c_str(),with.toString().c_str(),atAddr.toString().c_str()); @@ -1120,7 +1120,7 @@ bool IncomingPacket::_doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,const Sha redundant = peer->hasActivePathTo(now,a); } - if ( ((flags & ZT_PUSH_DIRECT_PATHS_FLAG_FORGET_PATH) == 0) && (!redundant) && (RR->node->shouldUsePathForZeroTierTraffic(_path->localAddress(),a)) ) { + if ( ((flags & ZT_PUSH_DIRECT_PATHS_FLAG_FORGET_PATH) == 0) && (!redundant) && (RR->node->shouldUsePathForZeroTierTraffic(peer->address(),_path->localAddress(),a)) ) { if (++countPerScope[(int)a.ipScope()][0] <= ZT_PUSH_DIRECT_PATHS_MAX_PER_SCOPE_AND_FAMILY) { TRACE("attempting to contact %s at pushed direct path %s",peer->address().toString().c_str(),a.toString().c_str()); peer->attemptToContactAt(InetAddress(),a,now); @@ -1139,7 +1139,7 @@ bool IncomingPacket::_doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,const Sha redundant = peer->hasActivePathTo(now,a); } - if ( ((flags & ZT_PUSH_DIRECT_PATHS_FLAG_FORGET_PATH) == 0) && (!redundant) && (RR->node->shouldUsePathForZeroTierTraffic(_path->localAddress(),a)) ) { + if ( ((flags & ZT_PUSH_DIRECT_PATHS_FLAG_FORGET_PATH) == 0) && (!redundant) && (RR->node->shouldUsePathForZeroTierTraffic(peer->address(),_path->localAddress(),a)) ) { if (++countPerScope[(int)a.ipScope()][1] <= ZT_PUSH_DIRECT_PATHS_MAX_PER_SCOPE_AND_FAMILY) { TRACE("attempting to contact %s at pushed direct path %s",peer->address().toString().c_str(),a.toString().c_str()); peer->attemptToContactAt(InetAddress(),a,now); diff --git a/node/Node.cpp b/node/Node.cpp index 263cfc6e..a180766b 100644 --- a/node/Node.cpp +++ b/node/Node.cpp @@ -46,34 +46,20 @@ namespace ZeroTier { /* Public Node interface (C++, exposed via CAPI bindings) */ /****************************************************************************/ -Node::Node( - uint64_t now, - void *uptr, - ZT_DataStoreGetFunction dataStoreGetFunction, - ZT_DataStorePutFunction dataStorePutFunction, - ZT_WirePacketSendFunction wirePacketSendFunction, - ZT_VirtualNetworkFrameFunction virtualNetworkFrameFunction, - ZT_VirtualNetworkConfigFunction virtualNetworkConfigFunction, - ZT_PathCheckFunction pathCheckFunction, - ZT_EventCallback eventCallback) : +Node::Node(void *uptr,const struct ZT_Node_Callbacks *callbacks,uint64_t now) : _RR(this), RR(&_RR), _uPtr(uptr), - _dataStoreGetFunction(dataStoreGetFunction), - _dataStorePutFunction(dataStorePutFunction), - _wirePacketSendFunction(wirePacketSendFunction), - _virtualNetworkFrameFunction(virtualNetworkFrameFunction), - _virtualNetworkConfigFunction(virtualNetworkConfigFunction), - _pathCheckFunction(pathCheckFunction), - _eventCallback(eventCallback), - _networks(), - _networks_m(), _prngStreamPtr(0), _now(now), _lastPingCheck(0), _lastHousekeepingRun(0), _relayPolicy(ZT_RELAY_POLICY_TRUSTED) { + if (callbacks->version != 0) + throw std::runtime_error("callbacks struct version mismatch"); + memcpy(&_cb,callbacks,sizeof(ZT_Node_Callbacks)); + _online = false; memset(_expectingRepliesToBucketPtr,0,sizeof(_expectingRepliesToBucketPtr)); @@ -81,30 +67,26 @@ Node::Node( memset(_lastIdentityVerification,0,sizeof(_lastIdentityVerification)); // Use Salsa20 alone as a high-quality non-crypto PRNG - { - char foo[32]; - Utils::getSecureRandom(foo,32); - _prng.init(foo,256,foo); - memset(_prngStream,0,sizeof(_prngStream)); - _prng.encrypt12(_prngStream,_prngStream,sizeof(_prngStream)); + char foo[32]; + Utils::getSecureRandom(foo,32); + _prng.init(foo,256,foo); + memset(_prngStream,0,sizeof(_prngStream)); + _prng.encrypt12(_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..."); + RR->identity.generate(); + idtmp = RR->identity.toString(true); + if (!dataStorePut("identity.secret",idtmp,true)) + throw std::runtime_error("unable to write identity.secret"); } - - { - std::string idtmp(dataStoreGet("identity.secret")); - if ((!idtmp.length())||(!RR->identity.fromString(idtmp))||(!RR->identity.hasPrivate())) { - TRACE("identity.secret not found, generating..."); - RR->identity.generate(); - idtmp = RR->identity.toString(true); - if (!dataStorePut("identity.secret",idtmp,true)) - throw std::runtime_error("unable to write identity.secret"); - } - RR->publicIdentityStr = RR->identity.toString(false); - RR->secretIdentityStr = RR->identity.toString(true); - idtmp = dataStoreGet("identity.public"); - if (idtmp != RR->publicIdentityStr) { - if (!dataStorePut("identity.public",RR->publicIdentityStr,false)) - throw std::runtime_error("unable to write identity.public"); - } + RR->publicIdentityStr = RR->identity.toString(false); + RR->secretIdentityStr = RR->identity.toString(true); + idtmp = dataStoreGet("identity.public"); + if (idtmp != RR->publicIdentityStr) { + if (!dataStorePut("identity.public",RR->publicIdentityStr,false)) + throw std::runtime_error("unable to write identity.public"); } try { @@ -638,7 +620,7 @@ std::string Node::dataStoreGet(const char *name) std::string r; unsigned long olen = 0; do { - long n = _dataStoreGetFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,name,buf,sizeof(buf),(unsigned long)r.length(),&olen); + long n = _cb.dataStoreGetFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,name,buf,sizeof(buf),(unsigned long)r.length(),&olen); if (n <= 0) return std::string(); r.append(buf,n); @@ -646,7 +628,7 @@ std::string Node::dataStoreGet(const char *name) return r; } -bool Node::shouldUsePathForZeroTierTraffic(const InetAddress &localAddress,const InetAddress &remoteAddress) +bool Node::shouldUsePathForZeroTierTraffic(const Address &ztaddr,const InetAddress &localAddress,const InetAddress &remoteAddress) { if (!Path::isAddressValidForPath(remoteAddress)) return false; @@ -663,9 +645,7 @@ bool Node::shouldUsePathForZeroTierTraffic(const InetAddress &localAddress,const } } - if (_pathCheckFunction) - return (_pathCheckFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,reinterpret_cast<const struct sockaddr_storage *>(&localAddress),reinterpret_cast<const struct sockaddr_storage *>(&remoteAddress)) != 0); - else return true; + return ( (_cb.pathCheckFunction) ? (_cb.pathCheckFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,ztaddr.toInt(),reinterpret_cast<const struct sockaddr_storage *>(&localAddress),reinterpret_cast<const struct sockaddr_storage *>(&remoteAddress)) != 0) : true); } #ifdef ZT_TRACE @@ -822,21 +802,11 @@ void Node::ncSendError(uint64_t nwid,uint64_t requestPacketId,const Address &des extern "C" { -enum ZT_ResultCode ZT_Node_new( - ZT_Node **node, - void *uptr, - uint64_t now, - ZT_DataStoreGetFunction dataStoreGetFunction, - ZT_DataStorePutFunction dataStorePutFunction, - ZT_WirePacketSendFunction wirePacketSendFunction, - ZT_VirtualNetworkFrameFunction virtualNetworkFrameFunction, - ZT_VirtualNetworkConfigFunction virtualNetworkConfigFunction, - ZT_PathCheckFunction pathCheckFunction, - ZT_EventCallback eventCallback) +enum ZT_ResultCode ZT_Node_new(ZT_Node **node,void *uptr,const struct ZT_Node_Callbacks *callbacks,uint64_t now) { *node = (ZT_Node *)0; try { - *node = reinterpret_cast<ZT_Node *>(new ZeroTier::Node(now,uptr,dataStoreGetFunction,dataStorePutFunction,wirePacketSendFunction,virtualNetworkFrameFunction,virtualNetworkConfigFunction,pathCheckFunction,eventCallback)); + *node = reinterpret_cast<ZT_Node *>(new ZeroTier::Node(uptr,callbacks,now)); return ZT_RESULT_OK; } catch (std::bad_alloc &exc) { return ZT_RESULT_FATAL_ERROR_OUT_OF_MEMORY; diff --git a/node/Node.hpp b/node/Node.hpp index 38303f8c..7d99ff09 100644 --- a/node/Node.hpp +++ b/node/Node.hpp @@ -59,17 +59,7 @@ namespace ZeroTier { class Node : public NetworkController::Sender { public: - Node( - uint64_t now, - void *uptr, - ZT_DataStoreGetFunction dataStoreGetFunction, - ZT_DataStorePutFunction dataStorePutFunction, - ZT_WirePacketSendFunction wirePacketSendFunction, - ZT_VirtualNetworkFrameFunction virtualNetworkFrameFunction, - ZT_VirtualNetworkConfigFunction virtualNetworkConfigFunction, - ZT_PathCheckFunction pathCheckFunction, - ZT_EventCallback eventCallback); - + Node(void *uptr,const struct ZT_Node_Callbacks *callbacks,uint64_t now); virtual ~Node(); // Public API Functions ---------------------------------------------------- @@ -127,24 +117,11 @@ public: // Internal functions ------------------------------------------------------ - /** - * @return Time as of last call to run() - */ inline uint64_t now() const throw() { return _now; } - /** - * Enqueue a ZeroTier message to be sent - * - * @param localAddress Local address - * @param addr Destination address - * @param data Packet data - * @param len Packet length - * @param ttl Desired TTL (default: 0 for unchanged/default TTL) - * @return True if packet appears to have been sent - */ inline bool putPacket(const InetAddress &localAddress,const InetAddress &addr,const void *data,unsigned int len,unsigned int ttl = 0) { - return (_wirePacketSendFunction( + return (_cb.wirePacketSendFunction( reinterpret_cast<ZT_Node *>(this), _uPtr, reinterpret_cast<const struct sockaddr_storage *>(&localAddress), @@ -154,21 +131,9 @@ public: ttl) == 0); } - /** - * Enqueue a frame to be injected into a tap device (port) - * - * @param nwid Network ID - * @param nuptr Network user ptr - * @param source Source MAC - * @param dest Destination MAC - * @param etherType 16-bit ethernet type - * @param vlanId VLAN ID or 0 if none - * @param data Frame data - * @param len Frame length - */ inline void putFrame(uint64_t nwid,void **nuptr,const MAC &source,const MAC &dest,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len) { - _virtualNetworkFrameFunction( + _cb.virtualNetworkFrameFunction( reinterpret_cast<ZT_Node *>(this), _uPtr, nwid, @@ -181,13 +146,6 @@ public: len); } - /** - * @param localAddress Local address - * @param remoteAddress Remote address - * @return True if path should be used - */ - bool shouldUsePathForZeroTierTraffic(const InetAddress &localAddress,const InetAddress &remoteAddress); - inline SharedPtr<Network> network(uint64_t nwid) const { Mutex::Lock _l(_networks_m); @@ -214,37 +172,20 @@ public: return nw; } - /** - * @return Potential direct paths to me a.k.a. local interface addresses - */ inline std::vector<InetAddress> 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<ZT_Node *>(this),_uPtr,name,data,len,(int)secure) == 0); } + inline bool dataStorePut(const char *name,const void *data,unsigned int len,bool secure) { return (_cb.dataStorePutFunction(reinterpret_cast<ZT_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<ZT_Node *>(this),_uPtr,name,(const void *)0,0,0); } + inline void dataStoreDelete(const char *name) { _cb.dataStorePutFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,name,(const void *)0,0,0); } std::string dataStoreGet(const char *name); - /** - * Post an event to the external user - * - * @param ev Event type - * @param md Meta-data (default: NULL/none) - */ - inline void postEvent(ZT_Event ev,const void *md = (const void *)0) { _eventCallback(reinterpret_cast<ZT_Node *>(this),_uPtr,ev,md); } + inline void postEvent(ZT_Event ev,const void *md = (const void *)0) { _cb.eventCallback(reinterpret_cast<ZT_Node *>(this),_uPtr,ev,md); } - /** - * Update virtual network port configuration - * - * @param nwid Network ID - * @param nuptr Network user ptr - * @param op Configuration operation - * @param nc Network configuration - */ - inline int configureVirtualNetworkPort(uint64_t nwid,void **nuptr,ZT_VirtualNetworkConfigOperation op,const ZT_VirtualNetworkConfig *nc) { return _virtualNetworkConfigFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,nwid,nuptr,op,nc); } + inline int configureVirtualNetworkPort(uint64_t nwid,void **nuptr,ZT_VirtualNetworkConfigOperation op,const ZT_VirtualNetworkConfig *nc) { return _cb.virtualNetworkConfigFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,nwid,nuptr,op,nc); } inline bool online() const throw() { return _online; } inline ZT_RelayPolicy relayPolicy() const { return _relayPolicy; } @@ -253,6 +194,9 @@ public: void postTrace(const char *module,unsigned int line,const char *fmt,...); #endif + bool shouldUsePathForZeroTierTraffic(const Address &ztaddr,const InetAddress &localAddress,const InetAddress &remoteAddress); + inline bool getPathHint(const Address &ztaddr,int family,InetAddress &addr) { return ( (_cb.pathLookupFunction) ? (_cb.pathLookupFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,ztaddr.toInt(),family,reinterpret_cast<struct sockaddr_storage *>(&addr)) != 0) : false ); } + uint64_t prng(); void postCircuitTestReport(const ZT_CircuitTestReport *report); void setTrustedPaths(const struct sockaddr_storage *networks,const uint64_t *ids,unsigned int count); @@ -317,8 +261,8 @@ private: RuntimeEnvironment _RR; RuntimeEnvironment *RR; - void *_uPtr; // _uptr (lower case) is reserved in Visual Studio :P + ZT_Node_Callbacks _cb; // For tracking packet IDs to filter out OK/ERROR replies to packets we did not send uint8_t _expectingRepliesToBucketPtr[ZT_EXPECTING_REPLIES_BUCKET_MASK1 + 1]; @@ -327,14 +271,6 @@ private: // Time of last identity verification indexed by InetAddress.rateGateHash() uint64_t _lastIdentityVerification[16384]; - ZT_DataStoreGetFunction _dataStoreGetFunction; - ZT_DataStorePutFunction _dataStorePutFunction; - ZT_WirePacketSendFunction _wirePacketSendFunction; - ZT_VirtualNetworkFrameFunction _virtualNetworkFrameFunction; - ZT_VirtualNetworkConfigFunction _virtualNetworkConfigFunction; - ZT_PathCheckFunction _pathCheckFunction; - ZT_EventCallback _eventCallback; - std::vector< std::pair< uint64_t, SharedPtr<Network> > > _networks; Mutex _networks_m; diff --git a/node/Peer.cpp b/node/Peer.cpp index 94fb5298..e0bd0eac 100644 --- a/node/Peer.cpp +++ b/node/Peer.cpp @@ -160,7 +160,7 @@ void Peer::received( } } - if ( (!pathIsConfirmed) && (RR->node->shouldUsePathForZeroTierTraffic(path->localAddress(),path->address())) ) { + if ( (!pathIsConfirmed) && (RR->node->shouldUsePathForZeroTierTraffic(_id.address(),path->localAddress(),path->address())) ) { if (verb == Packet::VERB_OK) { Mutex::Lock _l(_paths_m); diff --git a/node/Switch.cpp b/node/Switch.cpp index a5dd57e4..881d7b92 100644 --- a/node/Switch.cpp +++ b/node/Switch.cpp @@ -85,7 +85,7 @@ void Switch::onRemotePacket(const InetAddress &localAddr,const InetAddress &from Address beaconAddr(reinterpret_cast<const char *>(data) + 8,5); if (beaconAddr == RR->identity.address()) return; - if (!RR->node->shouldUsePathForZeroTierTraffic(localAddr,fromAddr)) + if (!RR->node->shouldUsePathForZeroTierTraffic(beaconAddr,localAddr,fromAddr)) return; SharedPtr<Peer> peer(RR->topology->getPeer(beaconAddr)); if (peer) { // we'll only respond to beacons from known peers diff --git a/service/OneService.cpp b/service/OneService.cpp index efb6ff3c..7434ca67 100644 --- a/service/OneService.cpp +++ b/service/OneService.cpp @@ -160,7 +160,6 @@ static uint64_t _jI(const json &jv,const uint64_t dfl) } return dfl; } -/* static bool _jB(const json &jv,const bool dfl) { if (jv.is_boolean()) { @@ -181,7 +180,6 @@ static bool _jB(const json &jv,const bool dfl) } return dfl; } -*/ static std::string _jS(const json &jv,const char *dfl) { if (jv.is_string()) { @@ -452,7 +450,8 @@ static long SnodeDataStoreGetFunction(ZT_Node *node,void *uptr,const char *name, static int SnodeDataStorePutFunction(ZT_Node *node,void *uptr,const char *name,const void *data,unsigned long len,int secure); static int SnodeWirePacketSendFunction(ZT_Node *node,void *uptr,const struct sockaddr_storage *localAddr,const struct sockaddr_storage *addr,const void *data,unsigned int len,unsigned int ttl); static void SnodeVirtualNetworkFrameFunction(ZT_Node *node,void *uptr,uint64_t nwid,void **nuptr,uint64_t sourceMac,uint64_t destMac,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len); -static int SnodePathCheckFunction(ZT_Node *node,void *uptr,const struct sockaddr_storage *localAddr,const struct sockaddr_storage *remoteAddr); +static int SnodePathCheckFunction(ZT_Node *node,void *uptr,uint64_t ztaddr,const struct sockaddr_storage *localAddr,const struct sockaddr_storage *remoteAddr); +static int SnodePathLookupFunction(ZT_Node *node,void *uptr,uint64_t ztaddr,int family,struct sockaddr_storage *result); #ifdef ZT_ENABLE_CLUSTER static void SclusterSendFunction(void *uptr,unsigned int toMemberId,const void *data,unsigned int len); @@ -536,11 +535,20 @@ public: const std::string _homePath; BackgroundResolver _tcpFallbackResolver; InetAddress _allowManagementFrom; - json _localConfig; EmbeddedNetworkController *_controller; Phy<OneServiceImpl *> _phy; Node *_node; + // Local configuration and memo-ized static path definitions + json _localConfig; + Hashtable< uint64_t,std::vector<InetAddress> > _v4Hints; + Hashtable< uint64_t,std::vector<InetAddress> > _v6Hints; + Hashtable< uint64_t,std::vector<InetAddress> > _v4Blacklists; + Hashtable< uint64_t,std::vector<InetAddress> > _v6Blacklists; + std::vector< InetAddress > _globalV4Blacklist; + std::vector< InetAddress > _globalV6Blacklist; + Mutex _localConfig_m; + /* * To attempt to handle NAT/gateway craziness we use three local UDP ports: * @@ -552,7 +560,6 @@ public: * destructively with uPnP port mapping behavior in very weird buggy ways. * It's only used if uPnP/NAT-PMP is enabled in this build. */ - Binder _bindings[3]; unsigned int _ports[3]; uint16_t _portsBE[3]; // ports in big-endian network byte order as in sockaddr @@ -756,16 +763,19 @@ public: // Clean up any legacy files if present OSUtils::rm((_homePath + ZT_PATH_SEPARATOR_S + "peers.save").c_str()); - _node = new Node( - OSUtils::now(), - this, - SnodeDataStoreGetFunction, - SnodeDataStorePutFunction, - SnodeWirePacketSendFunction, - SnodeVirtualNetworkFrameFunction, - SnodeVirtualNetworkConfigFunction, - SnodePathCheckFunction, - SnodeEventCallback); + { + struct ZT_Node_Callbacks cb; + cb.version = 0; + cb.dataStoreGetFunction = SnodeDataStoreGetFunction; + cb.dataStorePutFunction = SnodeDataStorePutFunction; + cb.wirePacketSendFunction = SnodeWirePacketSendFunction; + cb.virtualNetworkFrameFunction = SnodeVirtualNetworkFrameFunction; + cb.virtualNetworkConfigFunction = SnodeVirtualNetworkConfigFunction; + cb.eventCallback = SnodeEventCallback; + cb.pathCheckFunction = SnodePathCheckFunction; + cb.pathLookupFunction = SnodePathLookupFunction; + _node = new Node(this,&cb,OSUtils::now()); + } // Attempt to bind to a secondary port chosen from our ZeroTier address. // This exists because there are buggy NATs out there that fail if more @@ -842,6 +852,7 @@ public: } // Read local config file + Mutex::Lock _l2(_localConfig_m); std::string lcbuf; if (OSUtils::readFile((_homePath + ZT_PATH_SEPARATOR_S + "local.conf").c_str(),lcbuf)) { try { @@ -854,19 +865,18 @@ public: } } - // Get any trusted paths in local.conf + // Get any trusted paths in local.conf (we'll parse the rest of physical[] elsewhere) json &physical = _localConfig["physical"]; if (physical.is_object()) { for(json::iterator phy(physical.begin());phy!=physical.end();++phy) { - std::string nstr = phy.key(); - if (nstr.length()) { + InetAddress net(_jS(phy.key(),"")); + if (net) { if (phy.value().is_object()) { - uint64_t tpid = 0; - if ((tpid = _jI(phy.value()["trustedPathId"],0ULL))) { - InetAddress trustedPathNetwork(nstr); - if ( ((trustedPathNetwork.ss_family == AF_INET)||(trustedPathNetwork.ss_family == AF_INET6)) && (trustedPathCount < ZT_MAX_TRUSTED_PATHS) && (trustedPathNetwork.ipScope() != InetAddress::IP_SCOPE_GLOBAL) && (trustedPathNetwork.netmaskBits() > 0) ) { + uint64_t tpid; + if ((tpid = _jI(phy.value()["trustedPathId"],0ULL)) != 0ULL) { + if ( ((net.ss_family == AF_INET)||(net.ss_family == AF_INET6)) && (trustedPathCount < ZT_MAX_TRUSTED_PATHS) && (net.ipScope() != InetAddress::IP_SCOPE_GLOBAL) && (net.netmaskBits() > 0) ) { trustedPathIds[trustedPathCount] = tpid; - trustedPathNetworks[trustedPathCount] = trustedPathNetwork; + trustedPathNetworks[trustedPathCount] = net; ++trustedPathCount; } } @@ -878,31 +888,8 @@ public: // Set trusted paths if there are any if (trustedPathCount) _node->setTrustedPaths(reinterpret_cast<const struct sockaddr_storage *>(trustedPathNetworks),trustedPathIds,trustedPathCount); - - // Set any roles (upstream/federation) - json &virt = _localConfig["virtual"]; - if (virt.is_object()) { - for(json::iterator v(virt.begin());v!=virt.end();++v) { - const std::string nstr = v.key(); - if ((nstr.length() == ZT_ADDRESS_LENGTH_HEX)&&(v.value().is_object())) { - const Address ztaddr(nstr.c_str()); - if (ztaddr) - _node->setRole(ztaddr.toInt(),(_jS(v.value()["role"],"") == "upstream") ? ZT_PEER_ROLE_UPSTREAM : ZT_PEER_ROLE_LEAF); - } - } - } - - // Set any other local config stuff - json &settings = _localConfig["settings"]; - if (settings.is_object()) { - const std::string rp(_jS(settings["relayPolicy"],"")); - if (rp == "always") - _node->setRelayPolicy(ZT_RELAY_POLICY_ALWAYS); - else if (rp == "never") - _node->setRelayPolicy(ZT_RELAY_POLICY_NEVER); - else _node->setRelayPolicy(ZT_RELAY_POLICY_TRUSTED); - } } + applyLocalConfig(); _controller = new EmbeddedNetworkController(_node,(_homePath + ZT_PATH_SEPARATOR_S + ZT_CONTROLLER_DB_PATH).c_str()); _node->setNetconfMaster((void *)_controller); @@ -1174,7 +1161,90 @@ public: return true; } - // Begin private implementation methods + // Internal implementation methods ----------------------------------------- + + void applyLocalConfig() + { + Mutex::Lock _l(_localConfig_m); + + _v4Hints.clear(); + _v6Hints.clear(); + _v4Blacklists.clear(); + _v6Blacklists.clear(); + json &virt = _localConfig["virtual"]; + if (virt.is_object()) { + for(json::iterator v(virt.begin());v!=virt.end();++v) { + const std::string nstr = v.key(); + if ((nstr.length() == ZT_ADDRESS_LENGTH_HEX)&&(v.value().is_object())) { + const Address ztaddr(nstr.c_str()); + if (ztaddr) { + _node->setRole(ztaddr.toInt(),(_jS(v.value()["role"],"") == "upstream") ? ZT_PEER_ROLE_UPSTREAM : ZT_PEER_ROLE_LEAF); + + const uint64_t ztaddr2 = ztaddr.toInt(); + std::vector<InetAddress> &v4h = _v4Hints[ztaddr2]; + std::vector<InetAddress> &v6h = _v6Hints[ztaddr2]; + std::vector<InetAddress> &v4b = _v4Blacklists[ztaddr2]; + std::vector<InetAddress> &v6b = _v6Blacklists[ztaddr2]; + + json &tryAddrs = v.value()["try"]; + if (tryAddrs.is_array()) { + for(unsigned long i=0;i<tryAddrs.size();++i) { + const InetAddress ip(_jS(tryAddrs[i],"")); + if (ip.ss_family == AF_INET) + v4h.push_back(ip); + else if (ip.ss_family == AF_INET6) + v6h.push_back(ip); + } + } + json &blAddrs = v.value()["blacklist"]; + if (blAddrs.is_array()) { + for(unsigned long i=0;i<blAddrs.size();++i) { + const InetAddress ip(_jS(tryAddrs[i],"")); + if (ip.ss_family == AF_INET) + v4b.push_back(ip); + else if (ip.ss_family == AF_INET6) + v6b.push_back(ip); + } + } + + if (v4h.empty()) _v4Hints.erase(ztaddr2); + if (v6h.empty()) _v6Hints.erase(ztaddr2); + if (v4b.empty()) _v4Blacklists.erase(ztaddr2); + if (v6b.empty()) _v6Blacklists.erase(ztaddr2); + } + } + } + } + + _globalV4Blacklist.clear(); + _globalV6Blacklist.clear(); + json &physical = _localConfig["physical"]; + if (physical.is_object()) { + for(json::iterator phy(physical.begin());phy!=physical.end();++phy) { + const InetAddress net(_jS(phy.key(),"")); + if ((net)&&(net.netmaskBits() > 0)) { + if (phy.value().is_object()) { + if (_jB(phy.value()["blacklist"],false)) { + if (net.ss_family == AF_INET) + _globalV4Blacklist.push_back(net); + else if (net.ss_family == AF_INET6) + _globalV6Blacklist.push_back(net); + } + } + } + } + } + + json &settings = _localConfig["settings"]; + if (settings.is_object()) { + const std::string rp(_jS(settings["relayPolicy"],"")); + if (rp == "always") + _node->setRelayPolicy(ZT_RELAY_POLICY_ALWAYS); + else if (rp == "never") + _node->setRelayPolicy(ZT_RELAY_POLICY_NEVER); + else _node->setRelayPolicy(ZT_RELAY_POLICY_TRUSTED); + } + } // Checks if a managed IP or route target is allowed bool checkIfManagedIsAllowed(const NetworkState &n,const InetAddress &target) @@ -1306,6 +1376,8 @@ public: } } + // Handlers for Node and Phy<> callbacks ----------------------------------- + inline void phyOnDatagram(PhySocket *sock,void **uptr,const struct sockaddr *localAddr,const struct sockaddr *from,void *data,unsigned long len) { #ifdef ZT_ENABLE_CLUSTER @@ -1783,21 +1855,48 @@ public: n->tap->put(MAC(sourceMac),MAC(destMac),etherType,data,len); } - inline int nodePathCheckFunction(const struct sockaddr_storage *localAddr,const struct sockaddr_storage *remoteAddr) + inline int nodePathCheckFunction(uint64_t ztaddr,const struct sockaddr_storage *localAddr,const struct sockaddr_storage *remoteAddr) { - Mutex::Lock _l(_nets_m); - - for(std::map<uint64_t,NetworkState>::const_iterator n(_nets.begin());n!=_nets.end();++n) { - if (n->second.tap) { - std::vector<InetAddress> ips(n->second.tap->ips()); - for(std::vector<InetAddress>::const_iterator i(ips.begin());i!=ips.end();++i) { - if (i->containsAddress(*(reinterpret_cast<const InetAddress *>(remoteAddr)))) { - return 0; + // Make sure we're not trying to do ZeroTier-over-ZeroTier + { + Mutex::Lock _l(_nets_m); + for(std::map<uint64_t,NetworkState>::const_iterator n(_nets.begin());n!=_nets.end();++n) { + if (n->second.tap) { + std::vector<InetAddress> ips(n->second.tap->ips()); + for(std::vector<InetAddress>::const_iterator i(ips.begin());i!=ips.end();++i) { + if (i->containsAddress(*(reinterpret_cast<const InetAddress *>(remoteAddr)))) { + return 0; + } } } } } - + + // Check blacklists + const Hashtable< uint64_t,std::vector<InetAddress> > *blh = (const Hashtable< uint64_t,std::vector<InetAddress> > *)0; + const std::vector<InetAddress> *gbl = (const std::vector<InetAddress> *)0; + if (remoteAddr->ss_family == AF_INET) { + blh = &_v4Blacklists; + gbl = &_globalV4Blacklist; + } else if (remoteAddr->ss_family == AF_INET6) { + blh = &_v6Blacklists; + gbl = &_globalV6Blacklist; + } + if (blh) { + Mutex::Lock _l(_localConfig_m); + const std::vector<InetAddress> *l = blh->get(ztaddr); + if (l) { + for(std::vector<InetAddress>::const_iterator a(l->begin());a!=l->end();++a) { + if (a->containsAddress(*reinterpret_cast<const InetAddress *>(remoteAddr))) + return 0; + } + } + for(std::vector<InetAddress>::const_iterator a(gbl->begin());a!=gbl->end();++a) { + if (a->containsAddress(*reinterpret_cast<const InetAddress *>(remoteAddr))) + return 0; + } + } + /* Note: I do not think we need to scan for overlap with managed routes * because of the "route forking" and interface binding that we do. This * ensures (we hope) that ZeroTier traffic will still take the physical @@ -1807,6 +1906,23 @@ public: return 1; } + inline int nodePathLookupFunction(uint64_t ztaddr,int family,struct sockaddr_storage *result) + { + const Hashtable< uint64_t,std::vector<InetAddress> > *lh = (const Hashtable< uint64_t,std::vector<InetAddress> > *)0; + if (family < 0) + lh = (_node->prng() & 1) ? &_v4Hints : &_v6Hints; + else if (family == AF_INET) + lh = &_v4Hints; + else if (family == AF_INET6) + lh = &_v6Hints; + else return 0; + const std::vector<InetAddress> *l = lh->get(ztaddr); + if ((l)&&(l->size() > 0)) { + memcpy(result,&((*l)[(unsigned long)_node->prng() % l->size()]),sizeof(struct sockaddr_storage)); + return 1; + } else return 0; + } + inline void tapFrameHandler(uint64_t nwid,const MAC &from,const MAC &to,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len) { _node->processVirtualNetworkFrame(OSUtils::now(),nwid,from.toInt(),to.toInt(),etherType,vlanId,data,len,&_nextBackgroundTaskDeadline); @@ -1956,8 +2072,10 @@ static int SnodeWirePacketSendFunction(ZT_Node *node,void *uptr,const struct soc { return reinterpret_cast<OneServiceImpl *>(uptr)->nodeWirePacketSendFunction(localAddr,addr,data,len,ttl); } static void SnodeVirtualNetworkFrameFunction(ZT_Node *node,void *uptr,uint64_t nwid,void **nuptr,uint64_t sourceMac,uint64_t destMac,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len) { reinterpret_cast<OneServiceImpl *>(uptr)->nodeVirtualNetworkFrameFunction(nwid,nuptr,sourceMac,destMac,etherType,vlanId,data,len); } -static int SnodePathCheckFunction(ZT_Node *node,void *uptr,const struct sockaddr_storage *localAddr,const struct sockaddr_storage *remoteAddr) -{ return reinterpret_cast<OneServiceImpl *>(uptr)->nodePathCheckFunction(localAddr,remoteAddr); } +static int SnodePathCheckFunction(ZT_Node *node,void *uptr,uint64_t ztaddr,const struct sockaddr_storage *localAddr,const struct sockaddr_storage *remoteAddr) +{ return reinterpret_cast<OneServiceImpl *>(uptr)->nodePathCheckFunction(ztaddr,localAddr,remoteAddr); } +static int SnodePathLookupFunction(ZT_Node *node,void *uptr,uint64_t ztaddr,int family,struct sockaddr_storage *result) +{ return reinterpret_cast<OneServiceImpl *>(uptr)->nodePathLookupFunction(ztaddr,family,result); } #ifdef ZT_ENABLE_CLUSTER static void SclusterSendFunction(void *uptr,unsigned int toMemberId,const void *data,unsigned int len) |