/* * 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 "Containers.hpp" namespace ZeroTier { /** * 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: // Get rid of alignment warnings on 32-bit Windows #ifdef __WINDOWS__ void * operator new(size_t i) { return _mm_malloc(i,16); } void operator delete(void* p) { _mm_free(p); } #endif Node(void *uPtr, void *tPtr, const struct ZT_Node_Callbacks *callbacks, int64_t now); virtual ~Node(); void shutdown(void *tPtr); // Public API Functions --------------------------------------------------------------------------------------------- ZT_ResultCode processWirePacket( void *tPtr, int64_t now, int64_t localSocket, const struct sockaddr_storage *remoteAddress, SharedPtr< Buf > &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< Buf > &frameData, unsigned int frameLength, volatile int64_t *nextBackgroundTaskDeadline); ZT_ResultCode processHTTPResponse( void *tptr, int64_t now, void *requestId, int responseCode, const char **headerNames, const char **headerValues, const void *body, unsigned int bodySize, unsigned int flags); 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); 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 setInterfaceAddresses( const ZT_InterfaceAddress *addrs, unsigned int addrCount); ZT_ResultCode addPeer( void *tptr, const ZT_Identity *identity); int tryPeer( void *tptr, const ZT_Fingerprint *fp, const ZT_Endpoint *endpoint, int retries); ZT_CertificateError addCertificate( void *tptr, int64_t now, unsigned int localTrust, const ZT_Certificate *cert, const void *certData, unsigned int certSize); ZT_CertificateList *listCertificates(); 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 m_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 (m_cb.wirePacketSendFunction( reinterpret_cast(this), m_uPtr, tPtr, localSocket, &addr.as.ss, 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 { m_cb.virtualNetworkFrameFunction( reinterpret_cast(this), m_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 > network(const uint64_t nwid) const noexcept { RWMutex::RLock l(m_networks_l); Map< uint64_t, SharedPtr< Network > >::const_iterator n(m_networks.find(nwid)); if (likely(n != m_networks.end())) return n->second; return SharedPtr< Network >(); } /** * @return Known local interface addresses for this node */ ZT_INLINE Vector< ZT_InterfaceAddress > localInterfaceAddresses() const { Mutex::Lock _l(m_localInterfaceAddresses_m); return m_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 { m_cb.eventCallback(reinterpret_cast(this), m_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 { m_cb.virtualNetworkConfigFunction(reinterpret_cast(this), m_uPtr, tPtr, nwid, nuptr, op, nc); } /** * @return True if node appears online */ ZT_INLINE bool online() const noexcept { return m_online; } /** * Get a state object * * @param tPtr Thread pointer * @param type Object type to get * @param id Object ID or NULL if this type does not use one * @return Vector containing data or empty vector if not found or empty */ Vector< uint8_t > stateObjectGet(void *tPtr, ZT_StateObjectType type, const uint64_t *id); /** * 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 (m_cb.statePutFunction) m_cb.statePutFunction(reinterpret_cast(this), m_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 (m_cb.statePutFunction) m_cb.statePutFunction(reinterpret_cast(this), m_uPtr, tPtr, type, id, nullptr, -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); /** * @return This node's identity */ ZT_INLINE const Identity &identity() const noexcept { return m_RR.identity; } /** * 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 RevocationCredential &rev); virtual void ncSendError(uint64_t nwid, uint64_t requestPacketId, const Address &destination, NetworkController::ErrorCode errorCode); private: RuntimeEnvironment m_RR; RuntimeEnvironment *const RR; // Pointer to a struct defined in Node that holds instances of core objects. void *m_objects; // Function pointers to C callbacks supplied via the API. ZT_Node_Callbacks m_cb; // A user-specified opaque pointer passed back via API callbacks. void *m_uPtr; // Cache that remembers whether or not the locally running network controller (if any) has authorized // someone on their most recent query. This is used by the network controller as a memoization optimization // to elide unnecessary signature verifications. It might get moved in the future since this is sort of a // weird place to put it. struct p_LocalControllerAuth { uint64_t nwid, address; ZT_INLINE p_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 p_LocalControllerAuth &a) const noexcept { return ((a.nwid == nwid) && (a.address == address)); } ZT_INLINE bool operator!=(const p_LocalControllerAuth &a) const noexcept { return ((a.nwid != nwid) || (a.address != address)); } ZT_INLINE bool operator<(const p_LocalControllerAuth &a) const noexcept { return ((a.nwid < nwid) || ((a.nwid == nwid) && (a.address < address))); } }; Map< p_LocalControllerAuth, int64_t > m_localControllerAuthorizations; Mutex m_localControllerAuthorizations_l; // Locally joined networks by network ID. Map< uint64_t, SharedPtr< Network > > m_networks; RWMutex m_networks_l; // These are local interface addresses that have been configured via the API // and can be pushed to other nodes. Vector< ZT_InterfaceAddress > m_localInterfaceAddresses; Mutex m_localInterfaceAddresses_m; // This is locked while running processBackgroundTasks(). Mutex m_backgroundTasksLock; // These are locked via _backgroundTasksLock as they're only checked and modified in processBackgroundTasks(). int64_t m_lastPeerPulse; int64_t m_lastHousekeepingRun; int64_t m_lastNetworkHousekeepingRun; // This is the most recent value for time passed in via any of the core API methods. std::atomic< int64_t > m_now; // True if at least one root appears reachable. std::atomic< bool > m_online; }; } // namespace ZeroTier #endif