From ed2024285d13abb83366f11627418e7ddd6c4637 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Fri, 20 Sep 2019 14:11:15 -0700 Subject: [PATCH] More Go boilerplate. --- go/native/CMakeLists.txt | 4 +- go/native/{GoNode.cpp => GoGlue.cpp} | 75 ++++++++++++++++++++++++++-- go/native/{GoNode.h => GoGlue.h} | 53 ++++++++++++++++++-- include/ZeroTierCore.h | 13 +++++ node/MAC.hpp | 4 +- node/Node.cpp | 15 ++++++ node/Node.hpp | 1 + 7 files changed, 154 insertions(+), 11 deletions(-) rename go/native/{GoNode.cpp => GoGlue.cpp} (85%) rename go/native/{GoNode.h => GoGlue.h} (59%) diff --git a/go/native/CMakeLists.txt b/go/native/CMakeLists.txt index 4db0d57dc..96b0bb36f 100644 --- a/go/native/CMakeLists.txt +++ b/go/native/CMakeLists.txt @@ -2,11 +2,11 @@ cmake_minimum_required(VERSION 2.8) project(zt_go_native) set(src - GoNode.cpp + GoGlue.cpp ) set(headers - GoNode.h + GoGlue.h ) add_library(${PROJECT_NAME} STATIC ${src} ${headers}) diff --git a/go/native/GoNode.cpp b/go/native/GoGlue.cpp similarity index 85% rename from go/native/GoNode.cpp rename to go/native/GoGlue.cpp index c7872a39c..04451c880 100644 --- a/go/native/GoNode.cpp +++ b/go/native/GoGlue.cpp @@ -11,12 +11,14 @@ */ /****/ -#include "GoNode.h" +#include "GoGlue.h" #include "../../node/Constants.hpp" #include "../../node/InetAddress.hpp" #include "../../node/Node.hpp" #include "../../node/Utils.hpp" +#include "../../node/MAC.hpp" +#include "../../node/Address.hpp" #include "../../osdep/OSUtils.hpp" #include "../../osdep/BlockingQueue.hpp" #include "../../osdep/EthernetTap.hpp" @@ -55,6 +57,7 @@ #include #include #include +#include #ifdef __WINDOWS__ #define SETSOCKOPT_FLAG_TYPE BOOL @@ -90,10 +93,18 @@ struct ZT_GoNode_Impl int (*goPathLookupFunc)(ZT_GoNode *,ZT_Node *,int desiredAddressFamily,void *); int (*goStateObjectGetFunc)(ZT_GoNode *,ZT_Node *,int objType,const uint64_t id[2],void *buf,unsigned int bufSize); + std::string path; + std::atomic_bool run; + std::map< ZT_SOCKET,ZT_GoNodeThread > threads; std::mutex threads_l; + std::map< uint64_t,std::shared_ptr > taps; + std::mutex taps_l; + BlockingQueue eq; + + std::thread backgroundTaskThread; }; ////////////////////////////////////////////////////////////////////////////// @@ -273,10 +284,10 @@ static void ZT_GoNode_DNSResolver( ////////////////////////////////////////////////////////////////////////////// extern "C" ZT_GoNode *ZT_GoNode_new( + const char *workingPath, int (*goPathCheckFunc)(ZT_GoNode *,ZT_Node *,uint64_t ztAddress,const void *), int (*goPathLookupFunc)(ZT_GoNode *,ZT_Node *,int desiredAddressFamily,void *), - int (*goStateObjectGetFunc)(ZT_GoNode *,ZT_Node *,int objType,const uint64_t id[2],void *buf,unsigned int bufSize) -) + int (*goStateObjectGetFunc)(ZT_GoNode *,ZT_Node *,int objType,const uint64_t id[2],void *buf,unsigned int bufSize)) { try { struct ZT_Node_Callbacks cb; @@ -296,6 +307,18 @@ extern "C" ZT_GoNode *ZT_GoNode_new( gn->goPathCheckFunc = goPathCheckFunc; gn->goPathLookupFunc = goPathLookupFunc; gn->goStateObjectGetFunc = goStateObjectGetFunc; + gn->path = workingPath; + gn->run = true; + + gn->backgroundTaskThread = std::thread([gn] { + while (gn->run) { + std::this_thread::sleep_for(std::chrono::milliseconds(250)); + const int64_t now = OSUtils::now(); + if (now >= gn->nextBackgroundTaskDeadline) + gn->node->processBackgroundTasks(nullptr,now,&(gn->nextBackgroundTaskDeadline)); + } + }); + return gn; } catch ( ... ) { fprintf(stderr,"FATAL: unable to create new instance of Node (out of memory?)" ZT_EOL_S); @@ -305,6 +328,8 @@ extern "C" ZT_GoNode *ZT_GoNode_new( extern "C" void ZT_GoNode_delete(ZT_GoNode *gn) { + gn->run = false; + ZT_GoNodeEvent sd; sd.type = ZT_GONODE_EVENT_SHUTDOWN; gn->eq.post(sd); @@ -321,6 +346,14 @@ extern "C" void ZT_GoNode_delete(ZT_GoNode *gn) for(auto t=th.begin();t!=th.end();++t) t->join(); + gn->taps_l.lock(); + for(auto t=gn->taps.begin();t!=gn->taps.end();++t) + gn->node->leave(t->first,nullptr,nullptr); + gn->taps.clear(); + gn->taps_l.unlock(); + + gn->backgroundTaskThread.join(); + delete gn->node; delete gn; } @@ -491,3 +524,39 @@ extern "C" int ZT_GoNode_waitForEvent(ZT_GoNode *gn,ZT_GoNodeEvent *ev) { gn->eq.get(*ev); } + +static void tapFrameHandler(void *uptr,void *tptr,uint64_t nwid,const MAC &from,const MAC &to,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len) +{ + ZT_GoNode *const gn = reinterpret_cast(uptr); + gn->node->processVirtualNetworkFrame(tptr,OSUtils::now(),nwid,from.toInt(),to.toInt(),etherType,vlanId,data,len,&(gn->nextBackgroundTaskDeadline)); +} + +extern "C" ZT_GoTap *ZT_GoNode_join(ZT_GoNode *gn,uint64_t nwid) +{ + try { + std::lock_guard l(gn->taps_l); + auto existingTap = gn->taps.find(nwid); + if (existingTap != gn->taps.end()) + return (ZT_GoTap *)existingTap->second.get(); + char tmp[256]; + OSUtils::ztsnprintf(tmp,sizeof(tmp),"ZeroTier Network %.16llx",(unsigned long long)nwid); + std::shared_ptr tap(EthernetTap::newInstance(nullptr,gn->path.c_str(),MAC(Address(gn->node->address()),nwid),ZT_DEFAULT_MTU,0,nwid,tmp,&tapFrameHandler,gn)); + if (!tap) + return nullptr; + gn->taps[nwid] = tap; + gn->node->join(nwid,tap.get(),nullptr); + return (ZT_GoTap *)tap.get(); + } catch ( ... ) { + return nullptr; + } +} + +extern "C" void ZT_GoNode_leave(ZT_GoNode *gn,uint64_t nwid) +{ + std::lock_guard l(gn->taps_l); + auto existingTap = gn->taps.find(nwid); + if (existingTap != gn->taps.end()) { + gn->node->leave(nwid,nullptr,nullptr); + gn->taps.erase(existingTap); + } +} diff --git a/go/native/GoNode.h b/go/native/GoGlue.h similarity index 59% rename from go/native/GoNode.h rename to go/native/GoGlue.h index 285ea2226..cd94b799c 100644 --- a/go/native/GoNode.h +++ b/go/native/GoGlue.h @@ -20,6 +20,14 @@ #include "../../include/ZeroTierCore.h" +/****************************************************************************/ + +/* A pointer to an instance of EthernetTap */ +typedef void ZT_GoTap; + +/* ZT_GoNode is a C struct and functions that wraps ZT_Node for use via cgo. It + * performs UDP and other direct I/O in C for performance but otherwise lets + * the Go code control the node's behavior. */ struct ZT_GoNode_Impl; typedef struct ZT_GoNode_Impl ZT_GoNode; @@ -65,6 +73,7 @@ struct ZT_GoNodeEvent_Impl /* Network configuration update event */ struct { + ZT_GoTap *tap; int op; /* ZT_VirtualNetworkConfigOperation */ ZT_VirtualNetworkConfig conf; } nconf; @@ -73,27 +82,63 @@ struct ZT_GoNodeEvent_Impl typedef struct ZT_GoNodeEvent_Impl ZT_GoNodeEvent; -#ifndef __cplusplus +/****************************************************************************/ + +#ifdef __cplusplus extern "C" { #endif +/****************************************************************************/ + ZT_GoNode *ZT_GoNode_new( + const char *workingPath, int (*goPathCheckFunc)(ZT_GoNode *,ZT_Node *,uint64_t ztAddress,const void *), int (*goPathLookupFunc)(ZT_GoNode *,ZT_Node *,int desiredAddressFamily,void *), - int (*goStateObjectGetFunc)(ZT_GoNode *,ZT_Node *,int objType,const uint64_t id[2],void *buf,unsigned int bufSize) -); + int (*goStateObjectGetFunc)(ZT_GoNode *,ZT_Node *,int objType,const uint64_t id[2],void *buf,unsigned int bufSize)); void ZT_GoNode_delete(ZT_GoNode *gn); ZT_Node *ZT_GoNode_getNode(ZT_GoNode *gn); +/* This can be called more than once to start multiple listener threads */ int ZT_GoNode_phyStartListen(ZT_GoNode *gn,const char *dev,const char *ip,const int port); +/* Close all listener threads for a given local IP and port */ int ZT_GoNode_phyStopListen(ZT_GoNode *gn,const char *dev,const char *ip,const int port); int ZT_GoNode_waitForEvent(ZT_GoNode *gn,ZT_GoNodeEvent *ev); -#ifndef __cplusplus +ZT_GoTap *ZT_GoNode_join(ZT_GoNode *gn,uint64_t nwid); + +void ZT_GoNode_leave(ZT_GoNode *gn,uint64_t nwid); + +/****************************************************************************/ + +void ZT_GoTap_setEnabled(ZT_GoTap *tap,int enabled); + +int ZT_GoTap_addIp(ZT_GoTap *tap,int af,const void *ip,int port); + +int ZT_GoTap_removeIp(ZT_GoTap *tap,int af,const void *ip,int port); + +/* The buf buffer is filled with tuplies of: + * uint8_t family + * uint8_t ip[4 or 16] + * uint16_t port (big-endian byte order) + * + * This function returns the number of such tuples in the result. + * If the buffer isn't big enough results are incomplete. + */ +int ZT_GoTap_ips(ZT_GoTap *tap,void *buf,unsigned int bufSize); + +const char *ZT_GoTap_deviceName(ZT_GoTap *tap); + +void ZT_GoTap_setFriendlyName(ZT_GoTap *tap,const char *friendlyName); + +void ZT_GoTap_setMtu(ZT_GoTap *tap,unsigned int mtu); + +/****************************************************************************/ + +#ifdef __cplusplus } #endif diff --git a/include/ZeroTierCore.h b/include/ZeroTierCore.h index 375201942..06774f3c9 100644 --- a/include/ZeroTierCore.h +++ b/include/ZeroTierCore.h @@ -1851,6 +1851,7 @@ ZT_SDK_API void ZT_Node_processDNSResult( * @param node Node instance * @param nwid 64-bit ZeroTier network ID * @param uptr An arbitrary pointer to associate with this network (default: NULL) + * @param tptr Thread pointer to pass to functions/callbacks resulting from this call * @return OK (0) or error code if a fatal error condition has occurred */ ZT_SDK_API enum ZT_ResultCode ZT_Node_join(ZT_Node *node,uint64_t nwid,void *uptr,void *tptr); @@ -1868,6 +1869,7 @@ ZT_SDK_API enum ZT_ResultCode ZT_Node_join(ZT_Node *node,uint64_t nwid,void *upt * @param node Node instance * @param nwid 64-bit network ID * @param uptr Target pointer is set to uptr (if not NULL) + * @param tptr Thread pointer to pass to functions/callbacks resulting from this call * @return OK (0) or error code if a fatal error condition has occurred */ ZT_SDK_API enum ZT_ResultCode ZT_Node_leave(ZT_Node *node,uint64_t nwid,void **uptr,void *tptr); @@ -2015,6 +2017,17 @@ ZT_SDK_API ZT_VirtualNetworkConfig *ZT_Node_networkConfig(ZT_Node *node,uint64_t */ ZT_SDK_API ZT_VirtualNetworkList *ZT_Node_networks(ZT_Node *node); +/** + * Set the network-associated user-defined pointer for a given network + * + * This will have no effect if the network ID is not recognized. + * + * @param node Node instance + * @param nwid Network ID + * @param ptr New network-associated pointer + */ +ZT_SDK_API void ZT_Node_setNetworkUserPtr(ZT_Node *node,uint64_t nwid,void *ptr); + /** * Free a query result buffer * diff --git a/node/MAC.hpp b/node/MAC.hpp index 2256f7467..d200a7cad 100644 --- a/node/MAC.hpp +++ b/node/MAC.hpp @@ -41,8 +41,8 @@ public: ((((uint64_t)d) & 0xffULL) << 16) | ((((uint64_t)e) & 0xffULL) << 8) | (((uint64_t)f) & 0xffULL) ) {} - ZT_ALWAYS_INLINE MAC(const void *bits,unsigned int len) { setTo(bits,len); } - ZT_ALWAYS_INLINE MAC(const Address &ztaddr,uint64_t nwid) { fromAddress(ztaddr,nwid); } + ZT_ALWAYS_INLINE MAC(const void *bits,unsigned int len) { setTo(bits,len); } + ZT_ALWAYS_INLINE MAC(const Address &ztaddr,uint64_t nwid) { fromAddress(ztaddr,nwid); } ZT_ALWAYS_INLINE MAC(const uint64_t m) : _m(m & 0xffffffffffffULL) {} /** diff --git a/node/Node.cpp b/node/Node.cpp index 33ea617f2..32b664442 100644 --- a/node/Node.cpp +++ b/node/Node.cpp @@ -598,6 +598,14 @@ ZT_VirtualNetworkList *Node::networks() const return nl; } +void Node::setNetworkUserPtr(uint64_t nwid,void *ptr) +{ + Mutex::Lock _l(_networks_m); + const SharedPtr *const nw = _networks.get(nwid); + if (nw) + *((*nw)->userPtr()) = ptr; +} + void Node::freeQueryResult(void *qr) { if (qr) @@ -1011,6 +1019,13 @@ ZT_VirtualNetworkList *ZT_Node_networks(ZT_Node *node) } } +void ZT_Node_setNetworkUserPtr(ZT_Node *node,uint64_t nwid,void *ptr) +{ + try { + reinterpret_cast(node)->setNetworkUserPtr(nwid,ptr); + } catch ( ... ) {} +} + void ZT_Node_freeQueryResult(ZT_Node *node,void *qr) { try { diff --git a/node/Node.hpp b/node/Node.hpp index 4e55c35e1..fdfcb6201 100644 --- a/node/Node.hpp +++ b/node/Node.hpp @@ -102,6 +102,7 @@ public: 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); int addLocalInterfaceAddress(const struct sockaddr_storage *addr); void clearLocalInterfaceAddresses();