/* * Copyright (c)2013-2020 ZeroTier, Inc. * * Use of this software is governed by the Business Source License included * in the LICENSE.TXT file in the project's root directory. * * Change Date: 2024-01-01 * * On the date above, in accordance with the Business Source License, use * of this software will be governed by version 2.0 of the Apache License. */ /****/ #ifndef ZT_NODE_HPP #define ZT_NODE_HPP #include "Constants.hpp" #include "RuntimeEnvironment.hpp" #include "InetAddress.hpp" #include "Mutex.hpp" #include "MAC.hpp" #include "Network.hpp" #include "Path.hpp" #include "Salsa20.hpp" #include "NetworkController.hpp" #include "Buf.hpp" #include "Map.hpp" #include #include #include #include #include // Bit mask for "expecting reply" hash #define ZT_EXPECTING_REPLIES_BUCKET_MASK1 255 #define ZT_EXPECTING_REPLIES_BUCKET_MASK2 31 namespace ZeroTier { class Locator; /** * Implementation of Node object as defined in CAPI * * The pointer returned by ZT_Node_new() is an instance of this class. */ class Node : public NetworkController::Sender { public: Node(void *uPtr,void *tPtr,const struct ZT_Node_Callbacks *callbacks,int64_t now); virtual ~Node(); /** * Perform any operations that should be done prior to deleting a Node * * This is technically optional but recommended. * * @param tPtr Thread pointer to pass through to callbacks */ void shutdown(void *tPtr); // Get rid of alignment warnings on 32-bit Windows and possibly improve performance #ifdef __WINDOWS__ void * operator new(size_t i) { return _mm_malloc(i,16); } void operator delete(void* p) { _mm_free(p); } #endif // Public API Functions --------------------------------------------------------------------------------------------- ZT_ResultCode processWirePacket( void *tPtr, int64_t now, int64_t localSocket, const struct sockaddr_storage *remoteAddress, SharedPtr &packetData, unsigned int packetLength, volatile int64_t *nextBackgroundTaskDeadline); ZT_ResultCode processVirtualNetworkFrame( void *tPtr, int64_t now, uint64_t nwid, uint64_t sourceMac, uint64_t destMac, unsigned int etherType, unsigned int vlanId, SharedPtr &frameData, unsigned int frameLength, volatile int64_t *nextBackgroundTaskDeadline); ZT_ResultCode processBackgroundTasks(void *tPtr, int64_t now, volatile int64_t *nextBackgroundTaskDeadline); ZT_ResultCode join(uint64_t nwid,const ZT_Fingerprint *controllerFingerprint,void *uptr,void *tptr); ZT_ResultCode leave(uint64_t nwid,void **uptr,void *tptr); ZT_ResultCode multicastSubscribe(void *tPtr,uint64_t nwid,uint64_t multicastGroup,unsigned long multicastAdi); ZT_ResultCode multicastUnsubscribe(uint64_t nwid,uint64_t multicastGroup,unsigned long multicastAdi); ZT_ResultCode addRoot(void *tPtr,const ZT_Identity *identity,const sockaddr_storage *bootstrap); ZT_ResultCode removeRoot(void *tPtr,const ZT_Identity *identity); uint64_t address() const; void status(ZT_NodeStatus *status) const; ZT_PeerList *peers() const; ZT_VirtualNetworkConfig *networkConfig(uint64_t nwid) const; ZT_VirtualNetworkList *networks() const; void setNetworkUserPtr(uint64_t nwid,void *ptr); void freeQueryResult(void *qr); void setInterfaceAddresses(const ZT_InterfaceAddress *addrs,unsigned int addrCount); int sendUserMessage(void *tptr,uint64_t dest,uint64_t typeId,const void *data,unsigned int len); void setController(void *networkControllerInstance); // Internal functions ----------------------------------------------------------------------------------------------- /** * @return Most recent time value supplied to core via API */ ZT_INLINE int64_t now() const noexcept { return _now; } /** * Send packet to to the physical wire via callback * * @param tPtr Thread pointer * @param localSocket Local socket or -1 to use all/any * @param addr Destination address * @param data Data to send * @param len Length in bytes * @param ttl TTL or 0 for default/max * @return True if send appears successful */ ZT_INLINE bool putPacket(void *tPtr,const int64_t localSocket,const InetAddress &addr,const void *data,unsigned int len,unsigned int ttl = 0) noexcept { return (_cb.wirePacketSendFunction( reinterpret_cast(this), _uPtr, tPtr, localSocket, reinterpret_cast(&addr), data, len, ttl) == 0); } /** * Inject frame into virtual Ethernet tap * * @param tPtr Thread pointer * @param nwid Network ID * @param nuptr Network-associated user pointer * @param source Source MAC address * @param dest Destination MAC address * @param etherType 16-bit Ethernet type * @param vlanId Ethernet VLAN ID (currently unused) * @param data Ethernet frame data * @param len Ethernet frame length in bytes */ ZT_INLINE void putFrame(void *tPtr,uint64_t nwid,void **nuptr,const MAC &source,const MAC &dest,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len) noexcept { _cb.virtualNetworkFrameFunction( reinterpret_cast(this), _uPtr, tPtr, nwid, nuptr, source.toInt(), dest.toInt(), etherType, vlanId, data, len); } /** * @param nwid Network ID * @return Network associated with ID */ ZT_INLINE SharedPtr network(uint64_t nwid) const noexcept { RWMutex::RLock l(_networks_m); const SharedPtr *const n = _networks.get(nwid); if (n) return *n; return SharedPtr(); } /** * @return Known local interface addresses for this node */ ZT_INLINE std::vector localInterfaceAddresses() const { Mutex::Lock _l(_localInterfaceAddresses_m); return _localInterfaceAddresses; } /** * Post an event via external callback * * @param tPtr Thread pointer * @param ev Event object * @param md Event data or NULL if none */ ZT_INLINE void postEvent(void *tPtr,ZT_Event ev,const void *md = nullptr) noexcept { _cb.eventCallback(reinterpret_cast(this),_uPtr,tPtr,ev,md); } /** * Post network port configuration via external callback * * @param tPtr Thread pointer * @param nwid Network ID * @param nuptr Network-associated user pointer * @param op Config operation or event type * @param nc Network config info */ ZT_INLINE void configureVirtualNetworkPort(void *tPtr,uint64_t nwid,void **nuptr,ZT_VirtualNetworkConfigOperation op,const ZT_VirtualNetworkConfig *nc) noexcept { _cb.virtualNetworkConfigFunction(reinterpret_cast(this),_uPtr,tPtr,nwid,nuptr,op,nc); } /** * @return True if node appears online */ ZT_INLINE bool online() const noexcept { return _online; } /** * Get a state object * * @param tPtr Thread pointer * @param type Object type to get * @param id Object ID * @return Vector containing data or empty vector if not found or empty */ std::vector stateObjectGet(void *const tPtr,ZT_StateObjectType type,const uint64_t id[2]); /** * Store a state object * * @param tPtr Thread pointer * @param type Object type to get * @param id Object ID * @param data Data to store * @param len Length of data */ ZT_INLINE void stateObjectPut(void *const tPtr,ZT_StateObjectType type,const uint64_t id[2],const void *const data,const unsigned int len) noexcept { if (_cb.statePutFunction) _cb.statePutFunction(reinterpret_cast(this),_uPtr,tPtr,type,id,data,(int)len); } /** * Delete a state object * * @param tPtr Thread pointer * @param type Object type to delete * @param id Object ID */ ZT_INLINE void stateObjectDelete(void *const tPtr,ZT_StateObjectType type,const uint64_t id[2]) noexcept { if (_cb.statePutFunction) _cb.statePutFunction(reinterpret_cast(this),_uPtr,tPtr,type,id,(const void *)0,-1); } /** * Check whether a path should be used for ZeroTier traffic * * This performs internal checks and also calls out to an external callback if one is defined. * * @param tPtr Thread pointer * @param id Identity of peer * @param localSocket Local socket or -1 if unknown * @param remoteAddress Remote address * @return True if path should be used */ bool shouldUsePathForZeroTierTraffic(void *tPtr,const Identity &id,int64_t localSocket,const InetAddress &remoteAddress); /** * Query callback for a physical address for a peer * * @param tPtr Thread pointer * @param id Full identity of ZeroTier node * @param family Desired address family or -1 for any * @param addr Buffer to store address (result paramter) * @return True if addr was filled with something */ bool externalPathLookup(void *tPtr,const Identity &id,int family,InetAddress &addr); /** * Set physical path configuration * * @param pathNetwork Physical path network/netmask bits (CIDR notation) * @param pathConfig Path configuration * @return Return to pass through to external API */ ZT_ResultCode setPhysicalPathConfiguration(const struct sockaddr_storage *pathNetwork,const ZT_PhysicalPathConfiguration *pathConfig); /** * @return This node's identity */ ZT_INLINE const Identity &identity() const noexcept { return _RR.identity; } /** * @return True if aggressive NAT-traversal mechanisms like scanning of <1024 ports are enabled */ ZT_INLINE bool natMustDie() const noexcept { return _natMustDie; } /** * Wake any peers with the given address by calling their alarm() methods at or after the specified time * * @param peerAddress Peer address * @param triggerTime Time alarm should go off */ ZT_INLINE void setPeerAlarm(const Address &peerAddress,const int64_t triggerTime) { RWMutex::Lock l(_peerAlarms_l); int64_t &t = _peerAlarms[peerAddress]; if ((t <= 0)||(t > triggerTime)) t = triggerTime; } /** * Check whether a local controller has authorized a member on a network * * This is used by controllers to avoid needless certificate checks when we already * know if this has occurred. It's a bit of a hack but saves a massive amount of * controller CPU. It's easiest to put this here, and it imposes no overhead on * non-controllers. * * @param now Current time * @param nwid Network ID * @param addr Member address to check * @return True if member has been authorized */ bool localControllerHasAuthorized(int64_t now,uint64_t nwid,const Address &addr) const; // Implementation of NetworkController::Sender interface virtual void ncSendConfig(uint64_t nwid,uint64_t requestPacketId,const Address &destination,const NetworkConfig &nc,bool sendLegacyFormatConfig); virtual void ncSendRevocation(const Address &destination,const Revocation &rev); virtual void ncSendError(uint64_t nwid,uint64_t requestPacketId,const Address &destination,NetworkController::ErrorCode errorCode); private: RuntimeEnvironment _RR; void *_objects; RuntimeEnvironment *RR; ZT_Node_Callbacks _cb; void *_uPtr; // _uptr (lower case) is reserved in Visual Studio :P // Addresses of peers that want to have their alarm() function called at some point in the future. // These behave like weak references in that the node looks them up in Topology and calls alarm() // in each peer if that peer object is still held in memory. Calling alarm() unnecessarily on a peer // is harmless. This just exists as an optimization to prevent having to iterate through all peers // on every processBackgroundTasks call. A simple map<> is used here because there are usually only // a few of these, if any. std::map _peerAlarms; RWMutex _peerAlarms_l; // Map that remembers if we have recently sent a network config to someone // querying us as a controller. This is an optimization to allow network // controllers to know whether to treat things like multicast queries the // way authorized members would be treated without requiring an extra cert // validation. struct _LocalControllerAuth { uint64_t nwid,address; ZT_INLINE _LocalControllerAuth(const uint64_t nwid_,const Address &address_) noexcept: nwid(nwid_),address(address_.toInt()) {} ZT_INLINE unsigned long hashCode() const noexcept { return (unsigned long)(nwid + address); } ZT_INLINE bool operator==(const _LocalControllerAuth &a) const noexcept { return ((a.nwid == nwid) && (a.address == address)); } ZT_INLINE bool operator!=(const _LocalControllerAuth &a) const noexcept { return ((a.nwid != nwid) || (a.address != address)); } ZT_INLINE bool operator<(const _LocalControllerAuth &a) const noexcept { return ((a.nwid < nwid) || ((a.nwid == nwid)&&(a.address < address))); } }; Map<_LocalControllerAuth,int64_t> _localControllerAuthorizations; Mutex _localControllerAuthorizations_m; // Networks are stored in a flat hash table that is resized on any network ID collision. This makes // network lookup by network ID a few bitwise ops and an array index. Map< uint64_t,SharedPtr > _networks; RWMutex _networks_m; // These are local interface addresses that have been configured via the API // and can be pushed to other nodes. std::vector< ZT_InterfaceAddress > _localInterfaceAddresses; Mutex _localInterfaceAddresses_m; // This is locked while running processBackgroundTasks to ensure that calls to it are not concurrent. Mutex _backgroundTasksLock; volatile int64_t _now; volatile int64_t _lastPing; volatile int64_t _lastHousekeepingRun; volatile int64_t _lastNetworkHousekeepingRun; volatile int64_t _lastPathKeepaliveCheck; volatile bool _natMustDie; volatile bool _online; }; } // namespace ZeroTier #endif