Expand C API and simplify NodeService
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
* 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: 2025-01-01
|
||||
* Change Date: 2026-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.
|
||||
@@ -14,16 +14,20 @@
|
||||
/**
|
||||
* @file
|
||||
*
|
||||
* Header for ZeroTier Node Service (a distant relative of OneService)
|
||||
* ZeroTier Node Service
|
||||
*/
|
||||
|
||||
#ifndef ZT_NODE_SERVICE_HPP
|
||||
#define ZT_NODE_SERVICE_HPP
|
||||
#ifndef ZTS_NODE_SERVICE_HPP
|
||||
#define ZTS_NODE_SERVICE_HPP
|
||||
|
||||
#include "Binder.hpp"
|
||||
#include "Constants.hpp"
|
||||
#include "Events.hpp"
|
||||
#include "InetAddress.hpp"
|
||||
#include "Mutex.hpp"
|
||||
#include "Node.hpp"
|
||||
#include "PortMapper.hpp"
|
||||
#include "VirtualTap.hpp"
|
||||
#include "ZeroTierSockets.h"
|
||||
|
||||
#include <string>
|
||||
@@ -31,8 +35,9 @@
|
||||
|
||||
#define ZTS_SERVICE_THREAD_NAME "ZTServiceThread"
|
||||
#define ZTS_EVENT_CALLBACK_THREAD_NAME "ZTEventCallbackThread"
|
||||
// Interface metric for ZeroTier taps -- this ensures that if we are on WiFi and also
|
||||
// bridged via ZeroTier to the same LAN traffic will (if the OS is sane) prefer WiFi.
|
||||
// Interface metric for ZeroTier taps -- this ensures that if we are on WiFi and
|
||||
// also bridged via ZeroTier to the same LAN traffic will (if the OS is sane)
|
||||
// prefer WiFi.
|
||||
#define ZT_IF_METRIC 5000
|
||||
// How often to check for new multicast subscriptions on a tap device
|
||||
#define ZT_TAP_CHECK_MULTICAST_INTERVAL 5000
|
||||
@@ -40,21 +45,16 @@
|
||||
#define ZT_LOCAL_INTERFACE_CHECK_INTERVAL 60000
|
||||
|
||||
#ifdef __WINDOWS__
|
||||
#include <Windows.h>
|
||||
#include <Windows.h>
|
||||
#endif
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
/**
|
||||
* Local service for ZeroTier One as system VPN/NFV provider
|
||||
* ZeroTier node service
|
||||
*/
|
||||
class NodeService {
|
||||
public:
|
||||
uint16_t _userProvidedPort;
|
||||
std::string _userProvidedPath;
|
||||
char _userProvidedPublicIdentity[ZT_IDENTITY_STRING_BUFFER_LENGTH];
|
||||
char _userProvidedSecretIdentity[ZT_IDENTITY_STRING_BUFFER_LENGTH];
|
||||
|
||||
/**
|
||||
* Returned by node main if/when it terminates
|
||||
*/
|
||||
@@ -91,140 +91,455 @@ class NodeService {
|
||||
|
||||
/**
|
||||
* Whitelist of addresses that can be configured by this network.
|
||||
* If empty and allowManaged is true, allow all private/pseudoprivate addresses.
|
||||
* If empty and allowManaged is true, allow all
|
||||
* private/pseudoprivate addresses.
|
||||
*/
|
||||
std::vector<InetAddress> allowManagedWhitelist;
|
||||
|
||||
/**
|
||||
* Allow configuration of IPs and routes within global (Internet) IP space?
|
||||
* Allow configuration of IPs and routes within global (Internet) IP
|
||||
* space?
|
||||
*/
|
||||
bool allowGlobal;
|
||||
|
||||
/**
|
||||
* Allow overriding of system default routes for "full tunnel" operation?
|
||||
* Allow overriding of system default routes for "full tunnel"
|
||||
* operation?
|
||||
*/
|
||||
bool allowDefault;
|
||||
};
|
||||
|
||||
/**
|
||||
* @return Platform default home path or empty string if this platform doesn't have one
|
||||
*/
|
||||
static std::string platformDefaultHomePath();
|
||||
Phy<NodeService*> _phy;
|
||||
Node* _node;
|
||||
unsigned int _primaryPort = 0;
|
||||
unsigned int _secondaryPort = 0;
|
||||
unsigned int _tertiaryPort = 0;
|
||||
volatile unsigned int _udpPortPickerCounter;
|
||||
|
||||
/**
|
||||
* Create a new instance of the service
|
||||
std::map<uint64_t, unsigned int> peerCache;
|
||||
|
||||
// Local configuration and memo-ized information from it
|
||||
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;
|
||||
std::vector<InetAddress> _allowManagementFrom;
|
||||
std::vector<std::string> _interfacePrefixBlacklist;
|
||||
Mutex _localConfig_m;
|
||||
|
||||
std::vector<InetAddress> explicitBind;
|
||||
|
||||
/*
|
||||
* To attempt to handle NAT/gateway craziness we use three local UDP
|
||||
* ports:
|
||||
*
|
||||
* Once created, you must call the run() method to actually start
|
||||
* processing.
|
||||
* [0] is the normal/default port, usually 9993
|
||||
* [1] is a port derived from our ZeroTier address
|
||||
* [2] is a port computed from the normal/default for use with
|
||||
* uPnP/NAT-PMP mappings
|
||||
*
|
||||
* The port is saved to a file in the home path called zerotier-one.port,
|
||||
* which is used by the CLI and can be used to see which port was chosen if
|
||||
* 0 (random port) is picked.
|
||||
*
|
||||
* @param hp Home path
|
||||
* @param port TCP and UDP port for packets and HTTP control (if 0, pick random port)
|
||||
* [2] exists because on some gateways trying to do regular NAT-t
|
||||
* interferes destructively with uPnP port mapping behavior in very
|
||||
* weird buggy ways. It's only used if uPnP/NAT-PMP is enabled in this
|
||||
* build.
|
||||
*/
|
||||
static NodeService* newInstance(const char* hp, unsigned int port);
|
||||
unsigned int _ports[3] = { 0 };
|
||||
Binder _binder;
|
||||
|
||||
virtual ~NodeService();
|
||||
// Time we last received a packet from a global address
|
||||
uint64_t _lastDirectReceiveFromGlobal;
|
||||
|
||||
/**
|
||||
* Execute the service main I/O loop until terminated
|
||||
*
|
||||
* The terminate() method may be called from a signal handler or another
|
||||
* thread to terminate execution. Otherwise this will not return unless
|
||||
* another condition terminates execution such as a fatal error.
|
||||
*/
|
||||
virtual ReasonForTermination run() = 0;
|
||||
// Last potential sleep/wake event
|
||||
uint64_t _lastRestart;
|
||||
|
||||
/**
|
||||
* @return Reason for terminating or ONE_STILL_RUNNING if running
|
||||
*/
|
||||
virtual ReasonForTermination reasonForTermination() const = 0;
|
||||
// Deadline for the next background task service function
|
||||
volatile int64_t _nextBackgroundTaskDeadline;
|
||||
|
||||
/**
|
||||
* @return Fatal error message or empty string if none
|
||||
*/
|
||||
virtual std::string fatalErrorMessage() const = 0;
|
||||
// Configured networks
|
||||
struct NetworkState {
|
||||
NetworkState() : tap((VirtualTap*)0)
|
||||
{
|
||||
// Real defaults are in network 'up' code in network event
|
||||
// handler
|
||||
settings.allowManaged = true;
|
||||
settings.allowGlobal = false;
|
||||
settings.allowDefault = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return System device name corresponding with a given ZeroTier network ID or empty string if
|
||||
* not opened yet or network ID not found
|
||||
*/
|
||||
virtual std::string portDeviceName(uint64_t nwid) const = 0;
|
||||
VirtualTap* tap;
|
||||
ZT_VirtualNetworkConfig config; // memcpy() of raw config from core
|
||||
std::vector<InetAddress> managedIps;
|
||||
NetworkSettings settings;
|
||||
};
|
||||
std::map<uint64_t, NetworkState> _nets;
|
||||
|
||||
/**
|
||||
* Whether we allow access to the service via local HTTP requests (disabled by default in libzt)
|
||||
*/
|
||||
bool allowHttpBackplaneManagement = false;
|
||||
/** Lock to control access to network configuration data */
|
||||
Mutex _nets_m;
|
||||
/** Lock to control access to storage data */
|
||||
Mutex _store_m;
|
||||
/** Lock to control access to service run state */
|
||||
Mutex _run_m;
|
||||
// Set to false to force service to stop
|
||||
volatile bool _run;
|
||||
/** Lock to control access to termination reason */
|
||||
Mutex _termReason_m;
|
||||
// Termination status information
|
||||
ReasonForTermination _termReason;
|
||||
|
||||
/**
|
||||
* @return Reference to the Node
|
||||
*/
|
||||
virtual Node* getNode() = 0;
|
||||
std::string _fatalErrorMessage;
|
||||
|
||||
/**
|
||||
* Fills out a structure with network-specific route information
|
||||
*/
|
||||
virtual void getRoutes(uint64_t nwid, void* routeArray, unsigned int* numRoutes) = 0;
|
||||
|
||||
virtual void join(uint64_t nwid) = 0;
|
||||
virtual void leave(uint64_t nwid) = 0;
|
||||
virtual void getIdentity(char* key_pair_str, uint16_t* key_buf_len) = 0;
|
||||
|
||||
/**
|
||||
* Terminate background service (can be called from other threads)
|
||||
*/
|
||||
virtual void terminate() = 0;
|
||||
|
||||
/**
|
||||
* Get local settings for a network
|
||||
*
|
||||
* @param nwid Network ID
|
||||
* @param settings Buffer to fill with local network settings
|
||||
* @return True if network was found and settings is filled
|
||||
*/
|
||||
virtual bool getNetworkSettings(const uint64_t nwid, NetworkSettings& settings) const = 0;
|
||||
|
||||
/**
|
||||
* @return True if service is still running
|
||||
*/
|
||||
inline bool isRunning() const
|
||||
{
|
||||
return (this->reasonForTermination() == ONE_STILL_RUNNING);
|
||||
}
|
||||
|
||||
protected:
|
||||
NodeService()
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
NodeService(const NodeService& one)
|
||||
{
|
||||
}
|
||||
inline NodeService& operator=(const NodeService& one)
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
struct serviceParameters {
|
||||
int port;
|
||||
std::string path;
|
||||
char publicIdentityStr[ZT_IDENTITY_STRING_BUFFER_LENGTH];
|
||||
char secretIdentityStr[ZT_IDENTITY_STRING_BUFFER_LENGTH];
|
||||
};
|
||||
|
||||
#ifdef __WINDOWS__
|
||||
DWORD WINAPI _runNodeService(LPVOID arg);
|
||||
#else
|
||||
/**
|
||||
* NodeService thread
|
||||
*/
|
||||
void* _runNodeService(void* arg);
|
||||
// uPnP/NAT-PMP port mapper if enabled
|
||||
bool _portMappingEnabled; // local.conf settings
|
||||
#ifdef ZT_USE_MINIUPNPC
|
||||
PortMapper* _portMapper;
|
||||
#endif
|
||||
|
||||
/** Whether we allow caching network configs to storage */
|
||||
uint8_t _allowNetworkCaching;
|
||||
|
||||
/** Whether we allow caching peer hints to storage */
|
||||
uint8_t _allowPeerCaching;
|
||||
|
||||
char _publicIdStr[ZT_IDENTITY_STRING_BUFFER_LENGTH] = { 0 };
|
||||
char _secretIdStr[ZT_IDENTITY_STRING_BUFFER_LENGTH] = { 0 };
|
||||
char _planetData[ZTS_STORE_DATA_LEN] = { 0 };
|
||||
|
||||
/** Whether the node has successfully come online */
|
||||
bool _nodeIsOnline;
|
||||
|
||||
/** Whether we allow the NodeService to generate events for the user */
|
||||
bool _eventsEnabled;
|
||||
|
||||
/** Primary port defined by the user */
|
||||
uint16_t _userProvidedPort;
|
||||
|
||||
/** Storage path defined by the user */
|
||||
std::string _homePath;
|
||||
|
||||
/** System to ingest events from this class and emit them to the user */
|
||||
Events* _events;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
NodeService();
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
~NodeService();
|
||||
|
||||
ReasonForTermination run();
|
||||
|
||||
ReasonForTermination reasonForTermination() const;
|
||||
|
||||
std::string fatalErrorMessage() const;
|
||||
|
||||
void getRoutes(uint64_t net_id, void* routeArray, unsigned int* numRoutes);
|
||||
|
||||
void terminate();
|
||||
|
||||
void uninitialize();
|
||||
|
||||
int getNetworkSettings(const uint64_t net_id, NetworkSettings& settings) const;
|
||||
|
||||
int checkIfManagedIsAllowed(const NetworkState& n, const InetAddress& target);
|
||||
|
||||
void syncManagedStuff(NetworkState& n);
|
||||
|
||||
void phyOnDatagram(
|
||||
PhySocket* sock,
|
||||
void** uptr,
|
||||
const struct sockaddr* localAddr,
|
||||
const struct sockaddr* from,
|
||||
void* data,
|
||||
unsigned long len);
|
||||
|
||||
int nodeVirtualNetworkConfigFunction(
|
||||
uint64_t net_id,
|
||||
void** nuptr,
|
||||
enum ZT_VirtualNetworkConfigOperation op,
|
||||
const ZT_VirtualNetworkConfig* nwc);
|
||||
|
||||
void nodeEventCallback(enum ZT_Event event, const void* metaData);
|
||||
zts_net_info_t* prepare_network_details_msg(const NetworkState& n);
|
||||
|
||||
void generateEventMsgs();
|
||||
|
||||
/** Join a network */
|
||||
int join(uint64_t net_id);
|
||||
|
||||
/** Leave a network */
|
||||
int leave(uint64_t net_id);
|
||||
|
||||
/** Return number of networks joined */
|
||||
int networkCount();
|
||||
|
||||
/** Orbit a moon */
|
||||
int orbit(void* tptr, uint64_t moonWorldId, uint64_t moonSeed);
|
||||
|
||||
/** De-orbit a moon */
|
||||
int deorbit(void* tptr, uint64_t moonWorldId);
|
||||
|
||||
uint64_t getNodeId();
|
||||
|
||||
int getIdentity(char* keypair, uint16_t* len);
|
||||
|
||||
int setIdentity(const char* keypair, uint16_t len);
|
||||
|
||||
void nodeStatePutFunction(
|
||||
enum ZT_StateObjectType type,
|
||||
const uint64_t id[2],
|
||||
const void* data,
|
||||
int len);
|
||||
|
||||
int nodeStateGetFunction(
|
||||
enum ZT_StateObjectType type,
|
||||
const uint64_t id[2],
|
||||
void* data,
|
||||
unsigned int maxlen);
|
||||
|
||||
int nodeWirePacketSendFunction(
|
||||
const int64_t localSocket,
|
||||
const struct sockaddr_storage* addr,
|
||||
const void* data,
|
||||
unsigned int len,
|
||||
unsigned int ttl);
|
||||
|
||||
void nodeVirtualNetworkFrameFunction(
|
||||
uint64_t net_id,
|
||||
void** nuptr,
|
||||
uint64_t sourceMac,
|
||||
uint64_t destMac,
|
||||
unsigned int etherType,
|
||||
unsigned int vlanId,
|
||||
const void* data,
|
||||
unsigned int len);
|
||||
|
||||
int nodePathCheckFunction(
|
||||
uint64_t ztaddr,
|
||||
const int64_t localSocket,
|
||||
const struct sockaddr_storage* remoteAddr);
|
||||
|
||||
int nodePathLookupFunction(uint64_t ztaddr, int family, struct sockaddr_storage* result);
|
||||
|
||||
void tapFrameHandler(
|
||||
uint64_t net_id,
|
||||
const MAC& from,
|
||||
const MAC& to,
|
||||
unsigned int etherType,
|
||||
unsigned int vlanId,
|
||||
const void* data,
|
||||
unsigned int len);
|
||||
|
||||
int shouldBindInterface(const char* ifname, const InetAddress& ifaddr);
|
||||
|
||||
int _trialBind(unsigned int port);
|
||||
|
||||
/** Return whether the NodeService is running */
|
||||
int isRunning();
|
||||
|
||||
/** Return whether the node is online */
|
||||
int nodeIsOnline();
|
||||
|
||||
/** Instruct the NodeService on where to look for identity files and caches */
|
||||
int setHomePath(const char* homePath);
|
||||
|
||||
/** Set the NodeService's primary port */
|
||||
int setPrimaryPort(unsigned short primaryPort);
|
||||
|
||||
/** Get the NodeService's primary port */
|
||||
unsigned short getPrimaryPort();
|
||||
|
||||
/** Set the event system instance used to convey messages to the user */
|
||||
int setUserEventSystem(Events* events);
|
||||
|
||||
/** Set the planet definition */
|
||||
int setPlanet(const char* data, int len);
|
||||
|
||||
/** Add Interface prefix to blacklist (prevents ZeroTier from using that interface) */
|
||||
int addInterfacePrefixToBlacklist(const char* prefix, int len);
|
||||
|
||||
/** Return the MAC Address of the node in the given network */
|
||||
uint64_t getMACAddress(uint64_t net_id);
|
||||
|
||||
int getNetworkName(uint64_t net_id, char* dst, int len);
|
||||
|
||||
int allowPeerCaching(int allowed);
|
||||
int allowNetworkCaching(int allowed);
|
||||
int getNetworkBroadcast(uint64_t net_id);
|
||||
int getNetworkMTU(uint64_t net_id);
|
||||
int getNetworkType(uint64_t net_id);
|
||||
int getNetworkStatus(uint64_t net_id);
|
||||
|
||||
int getFirstAssignedAddr(uint64_t net_id, int family, struct zts_sockaddr_storage* addr);
|
||||
|
||||
int getAllAssignedAddr(uint64_t net_id, struct zts_sockaddr_storage* addr, int* count);
|
||||
|
||||
int networkHasRoute(uint64_t net_id, int family);
|
||||
|
||||
int addrIsAssigned(uint64_t net_id, int family);
|
||||
|
||||
void phyOnTcpConnect(PhySocket* sock, void** uptr, bool success)
|
||||
{
|
||||
}
|
||||
void phyOnTcpAccept(
|
||||
PhySocket* sockL,
|
||||
PhySocket* sockN,
|
||||
void** uptrL,
|
||||
void** uptrN,
|
||||
const struct sockaddr* from)
|
||||
{
|
||||
}
|
||||
void phyOnTcpClose(PhySocket* sock, void** uptr)
|
||||
{
|
||||
}
|
||||
void phyOnTcpData(PhySocket* sock, void** uptr, void* data, unsigned long len)
|
||||
{
|
||||
}
|
||||
void phyOnTcpWritable(PhySocket* sock, void** uptr)
|
||||
{
|
||||
}
|
||||
void phyOnFileDescriptorActivity(PhySocket* sock, void** uptr, bool readable, bool writable)
|
||||
{
|
||||
}
|
||||
void phyOnUnixAccept(PhySocket* sockL, PhySocket* sockN, void** uptrL, void** uptrN)
|
||||
{
|
||||
}
|
||||
void phyOnUnixClose(PhySocket* sock, void** uptr)
|
||||
{
|
||||
}
|
||||
void phyOnUnixData(PhySocket* sock, void** uptr, void* data, unsigned long len)
|
||||
{
|
||||
}
|
||||
void phyOnUnixWritable(PhySocket* sock, void** uptr)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
static int SnodeVirtualNetworkConfigFunction(
|
||||
ZT_Node* node,
|
||||
void* uptr,
|
||||
void* tptr,
|
||||
uint64_t net_id,
|
||||
void** nuptr,
|
||||
enum ZT_VirtualNetworkConfigOperation op,
|
||||
const ZT_VirtualNetworkConfig* nwconf)
|
||||
{
|
||||
return reinterpret_cast<NodeService*>(uptr)
|
||||
->nodeVirtualNetworkConfigFunction(net_id, nuptr, op, nwconf);
|
||||
}
|
||||
|
||||
static void
|
||||
SnodeEventCallback(ZT_Node* node, void* uptr, void* tptr, enum ZT_Event event, const void* metaData)
|
||||
{
|
||||
reinterpret_cast<NodeService*>(uptr)->nodeEventCallback(event, metaData);
|
||||
}
|
||||
|
||||
static void SnodeStatePutFunction(
|
||||
ZT_Node* node,
|
||||
void* uptr,
|
||||
void* tptr,
|
||||
enum ZT_StateObjectType type,
|
||||
const uint64_t id[2],
|
||||
const void* data,
|
||||
int len)
|
||||
{
|
||||
reinterpret_cast<NodeService*>(uptr)->nodeStatePutFunction(type, id, data, len);
|
||||
}
|
||||
|
||||
static int SnodeStateGetFunction(
|
||||
ZT_Node* node,
|
||||
void* uptr,
|
||||
void* tptr,
|
||||
enum ZT_StateObjectType type,
|
||||
const uint64_t id[2],
|
||||
void* data,
|
||||
unsigned int maxlen)
|
||||
{
|
||||
return reinterpret_cast<NodeService*>(uptr)->nodeStateGetFunction(type, id, data, maxlen);
|
||||
}
|
||||
|
||||
static int SnodeWirePacketSendFunction(
|
||||
ZT_Node* node,
|
||||
void* uptr,
|
||||
void* tptr,
|
||||
int64_t localSocket,
|
||||
const struct sockaddr_storage* addr,
|
||||
const void* data,
|
||||
unsigned int len,
|
||||
unsigned int ttl)
|
||||
{
|
||||
return reinterpret_cast<NodeService*>(uptr)
|
||||
->nodeWirePacketSendFunction(localSocket, addr, data, len, ttl);
|
||||
}
|
||||
|
||||
static void SnodeVirtualNetworkFrameFunction(
|
||||
ZT_Node* node,
|
||||
void* uptr,
|
||||
void* tptr,
|
||||
uint64_t net_id,
|
||||
void** nuptr,
|
||||
uint64_t sourceMac,
|
||||
uint64_t destMac,
|
||||
unsigned int etherType,
|
||||
unsigned int vlanId,
|
||||
const void* data,
|
||||
unsigned int len)
|
||||
{
|
||||
reinterpret_cast<NodeService*>(uptr)->nodeVirtualNetworkFrameFunction(
|
||||
net_id,
|
||||
nuptr,
|
||||
sourceMac,
|
||||
destMac,
|
||||
etherType,
|
||||
vlanId,
|
||||
data,
|
||||
len);
|
||||
}
|
||||
|
||||
static int SnodePathCheckFunction(
|
||||
ZT_Node* node,
|
||||
void* uptr,
|
||||
void* tptr,
|
||||
uint64_t ztaddr,
|
||||
int64_t localSocket,
|
||||
const struct sockaddr_storage* remoteAddr)
|
||||
{
|
||||
return reinterpret_cast<NodeService*>(uptr)->nodePathCheckFunction(
|
||||
ztaddr,
|
||||
localSocket,
|
||||
remoteAddr);
|
||||
}
|
||||
|
||||
static int SnodePathLookupFunction(
|
||||
ZT_Node* node,
|
||||
void* uptr,
|
||||
void* tptr,
|
||||
uint64_t ztaddr,
|
||||
int family,
|
||||
struct sockaddr_storage* result)
|
||||
{
|
||||
return reinterpret_cast<NodeService*>(uptr)->nodePathLookupFunction(ztaddr, family, result);
|
||||
}
|
||||
|
||||
static void StapFrameHandler(
|
||||
void* uptr,
|
||||
void* tptr,
|
||||
uint64_t net_id,
|
||||
const MAC& from,
|
||||
const MAC& to,
|
||||
unsigned int etherType,
|
||||
unsigned int vlanId,
|
||||
const void* data,
|
||||
unsigned int len)
|
||||
{
|
||||
reinterpret_cast<NodeService*>(uptr)
|
||||
->tapFrameHandler(net_id, from, to, etherType, vlanId, data, len);
|
||||
}
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user