Adam Ierymenko 5 years ago
parent
commit
b34aa10bf8

+ 1 - 1
go/go.mod

@@ -1,3 +1,3 @@
-module zerotier-go
+module zerotier
 
 go 1.13

+ 0 - 0
go/go.sum


+ 142 - 61
go/native/GoGlue.cpp

@@ -20,7 +20,6 @@
 #include "../../node/MAC.hpp"
 #include "../../node/Address.hpp"
 #include "../../osdep/OSUtils.hpp"
-#include "../../osdep/BlockingQueue.hpp"
 #include "../../osdep/EthernetTap.hpp"
 
 #include <string.h>
@@ -89,10 +88,6 @@ struct ZT_GoNode_Impl
 	Node *node;
 	volatile int64_t nextBackgroundTaskDeadline;
 
-	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);
-
 	std::string path;
 	std::atomic_bool run;
 
@@ -102,12 +97,19 @@ struct ZT_GoNode_Impl
 	std::map< uint64_t,std::shared_ptr<EthernetTap> > taps;
 	std::mutex taps_l;
 
-	BlockingQueue<ZT_GoNodeEvent> eq;
-
 	std::thread backgroundTaskThread;
 };
 
-//////////////////////////////////////////////////////////////////////////////
+/****************************************************************************/
+
+/* These functions are implemented in Go in pkg/ztnode/node-callbacks.go */
+extern "C" int goPathCheckFunc(ZT_GoNode *,uint64_t,int,const void *,int);
+extern "C" int goPathLookupFunc(ZT_GoNode *,uint64_t,int,int *,uint8_t [16],int *);
+extern "C" void goStateObjectPutFunc(ZT_GoNode *,int,const uint64_t [2],const void *,int);
+extern "C" int goStateObjectGetFunc(ZT_GoNode *,int,const uint64_t [2],void *,unsigned int);
+extern "C" void goDNSResolverFunc(ZT_GoNode *,const uint8_t *,int,const char *,uintptr_t);
+extern "C" int goVirtualNetworkConfigFunc(ZT_GoNode *,ZT_GoTap *,uint64_t,int,const ZT_VirtualNetworkConfig *);
+extern "C" void goZtEvent(ZT_GoNode *,int,const void *);
 
 static int ZT_GoNode_VirtualNetworkConfigFunction(
 	ZT_Node *node,
@@ -118,13 +120,7 @@ static int ZT_GoNode_VirtualNetworkConfigFunction(
 	enum ZT_VirtualNetworkConfigOperation op,
 	const ZT_VirtualNetworkConfig *cfg)
 {
-	ZT_GoNodeEvent ev;
-	ev.type = ZT_GONODE_EVENT_NETWORK_CONFIG_UPDATE;
-	ev.data.nconf.op = op;
-	if (cfg)
-		ev.data.nconf.conf = *cfg;
-	reinterpret_cast<ZT_GoNode *>(uptr)->eq.post(ev);
-	return 0;
+	return goVirtualNetworkConfigFunc(reinterpret_cast<ZT_GoNode *>(uptr),reinterpret_cast<ZT_GoTap *>(*nptr),nwid,op,cfg);
 }
 
 static void ZT_GoNode_VirtualNetworkFrameFunction(
@@ -151,10 +147,7 @@ static void ZT_GoNode_EventCallback(
 	enum ZT_Event et,
 	const void *data)
 {
-	ZT_GoNodeEvent ev;
-	ev.type = ZT_GONODE_EVENT_ZTEVENT;
-	ev.data.zt.type = et;
-	reinterpret_cast<ZT_GoNode *>(uptr)->eq.post(ev);
+	goZtEvent(reinterpret_cast<ZT_GoNode *>(uptr),et,data);
 }
 
 static void ZT_GoNode_StatePutFunction(
@@ -166,18 +159,7 @@ static void ZT_GoNode_StatePutFunction(
 	const void *data,
 	int len)
 {
-	if (len < ZT_MAX_STATE_OBJECT_SIZE) { // sanity check
-		ZT_GoNodeEvent ev;
-		ev.type = (len >= 0) ? ZT_GONODE_EVENT_STATE_PUT : ZT_GONODE_EVENT_STATE_DELETE;
-		if (len > 0) {
-			memcpy(ev.data.sobj.data,data,len);
-			ev.data.sobj.len = (unsigned int)len;
-		}
-		ev.data.sobj.objType = objType;
-		ev.data.sobj.id[0] = id[0];
-		ev.data.sobj.id[1] = id[1];
-		reinterpret_cast<ZT_GoNode *>(uptr)->eq.post(ev);
-	}
+	goStateObjectPutFunc(reinterpret_cast<ZT_GoNode *>(uptr),objType,id,data,len);
 }
 
 static int ZT_GoNode_StateGetFunction(
@@ -189,7 +171,12 @@ static int ZT_GoNode_StateGetFunction(
 	void *buf,
 	unsigned int buflen)
 {
-	return reinterpret_cast<ZT_GoNode *>(uptr)->goStateObjectGetFunc(reinterpret_cast<ZT_GoNode *>(uptr),reinterpret_cast<ZT_GoNode *>(uptr)->node,(int)objType,id,buf,buflen);
+	return goStateObjectGetFunc(
+		reinterpret_cast<ZT_GoNode *>(uptr),
+		(int)objType,
+		id,
+		buf,
+		buflen);
 }
 
 static ZT_ALWAYS_INLINE void doUdpSend(ZT_SOCKET sock,const struct sockaddr_storage *addr,const void *data,const unsigned int len,const unsigned int ipTTL)
@@ -254,7 +241,23 @@ static int ZT_GoNode_PathCheckFunction(
 	int64_t localSocket,
 	const struct sockaddr_storage *sa)
 {
-	return reinterpret_cast<ZT_GoNode *>(uptr)->goPathCheckFunc(reinterpret_cast<ZT_GoNode *>(uptr),reinterpret_cast<ZT_GoNode *>(uptr)->node,ztAddress,sa);
+	switch(sa->ss_family) {
+		case AF_INET:
+			return goPathCheckFunc(
+				reinterpret_cast<ZT_GoNode *>(uptr),
+				ztAddress,
+				AF_INET,
+				&(reinterpret_cast<const struct sockaddr_in *>(sa)->sin_addr.s_addr),
+				Utils::ntoh((uint16_t)reinterpret_cast<const struct sockaddr_in *>(sa)->sin_port));
+		case AF_INET6:
+			return goPathCheckFunc(
+				reinterpret_cast<ZT_GoNode *>(uptr),
+				ztAddress,
+				AF_INET6,
+				reinterpret_cast<const struct sockaddr_in6 *>(sa)->sin6_addr.s6_addr,
+				Utils::ntoh((uint16_t)reinterpret_cast<const struct sockaddr_in6 *>(sa)->sin6_port));
+	}
+	return 0;
 }
 
 static int ZT_GoNode_PathLookupFunction(
@@ -265,7 +268,32 @@ static int ZT_GoNode_PathLookupFunction(
 	int desiredAddressFamily,
 	struct sockaddr_storage *sa)
 {
-	return reinterpret_cast<ZT_GoNode *>(uptr)->goPathLookupFunc(reinterpret_cast<ZT_GoNode *>(uptr),reinterpret_cast<ZT_GoNode *>(uptr)->node,desiredAddressFamily,sa);
+	int family = 0;
+	uint8_t ip[16];
+	int port = 0;
+	const int result = goPathLookupFunc(
+		reinterpret_cast<ZT_GoNode *>(uptr),
+		ztAddress,
+		desiredAddressFamily,
+		&family,
+		ip,
+		&port
+	);
+	if (result != 0) {
+		switch(family) {
+			case AF_INET:
+				reinterpret_cast<struct sockaddr_in *>(sa)->sin_family = AF_INET;
+				memcpy(&(reinterpret_cast<struct sockaddr_in *>(sa)->sin_addr.s_addr),ip,4);
+				reinterpret_cast<struct sockaddr_in *>(sa)->sin_port = Utils::hton((uint16_t)port);
+				return 1;
+			case AF_INET6:
+				reinterpret_cast<struct sockaddr_in6 *>(sa)->sin6_family = AF_INET6;
+				memcpy(reinterpret_cast<struct sockaddr_in6 *>(sa)->sin6_addr.s6_addr,ip,16);
+				reinterpret_cast<struct sockaddr_in6 *>(sa)->sin6_port = Utils::hton((uint16_t)port);
+				return 1;
+		}
+	}
+	return 0;
 }
 
 static void ZT_GoNode_DNSResolver(
@@ -277,19 +305,14 @@ static void ZT_GoNode_DNSResolver(
 	const char *name,
 	uintptr_t requestId)
 {
-	ZT_GoNodeEvent ev;
-	ev.type = ZT_GONODE_EVENT_DNS_GET_TXT;
-	Utils::scopy(ev.data.dns.dnsName,sizeof(ev.data.dns.dnsName),name);
-	reinterpret_cast<ZT_GoNode *>(uptr)->eq.post(ev);
+	uint8_t t[256];
+	for(unsigned int i=0;(i<numTypes)&&(i<256);++i) t[i] = (uint8_t)types[i];
+	goDNSResolverFunc(reinterpret_cast<ZT_GoNode *>(uptr),t,(int)numTypes,name,requestId);
 }
 
-//////////////////////////////////////////////////////////////////////////////
+/****************************************************************************/
 
-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))
+extern "C" ZT_GoNode *ZT_GoNode_new(const char *workingPath)
 {
 	try {
 		struct ZT_Node_Callbacks cb;
@@ -307,9 +330,6 @@ extern "C" ZT_GoNode *ZT_GoNode_new(
 		const int64_t now = OSUtils::now();
 		gn->node = new Node(reinterpret_cast<void *>(gn),nullptr,&cb,now);
 		gn->nextBackgroundTaskDeadline = now;
-		gn->goPathCheckFunc = goPathCheckFunc;
-		gn->goPathLookupFunc = goPathLookupFunc;
-		gn->goStateObjectGetFunc = goStateObjectGetFunc;
 		gn->path = workingPath;
 		gn->run = true;
 
@@ -333,10 +353,6 @@ 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);
-
 	gn->threads_l.lock();
 	for(auto t=gn->threads.begin();t!=gn->threads.end();++t) {
 		t->second.run = false;
@@ -358,11 +374,6 @@ extern "C" void ZT_GoNode_delete(ZT_GoNode *gn)
 	delete gn;
 }
 
-extern "C" ZT_Node *ZT_GoNode_getNode(ZT_GoNode *gn)
-{
-	return gn->node;
-}
-
 // Sets flags and socket options common to both IPv4 and IPv6 UDP sockets
 static void setCommonUdpSocketSettings(ZT_SOCKET udpSock,const char *dev)
 {
@@ -521,11 +532,6 @@ extern "C" int ZT_GoNode_phyStopListen(ZT_GoNode *gn,const char *dev,const char
 	return 0;
 }
 
-extern "C" void 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<ZT_GoNode *>(uptr);
@@ -561,3 +567,78 @@ extern "C" void ZT_GoNode_leave(ZT_GoNode *gn,uint64_t nwid)
 		gn->taps.erase(existingTap);
 	}
 }
+
+/****************************************************************************/
+
+extern "C" void ZT_GoTap_setEnabled(ZT_GoTap *tap,int enabled)
+{
+	reinterpret_cast<EthernetTap *>(tap)->setEnabled(enabled != 0);
+}
+
+extern "C" int ZT_GoTap_addIp(ZT_GoTap *tap,int af,const void *ip,int port)
+{
+	switch(af) {
+		case AF_INET:
+			return (reinterpret_cast<EthernetTap *>(tap)->addIp(InetAddress(ip,4,(unsigned int)port)) ? 1 : 0);
+		case AF_INET6:
+			return (reinterpret_cast<EthernetTap *>(tap)->addIp(InetAddress(ip,16,(unsigned int)port)) ? 1 : 0);
+	}
+	return 0;
+}
+
+extern "C" int ZT_GoTap_removeIp(ZT_GoTap *tap,int af,const void *ip,int port)
+{
+	switch(af) {
+		case AF_INET:
+			return (reinterpret_cast<EthernetTap *>(tap)->removeIp(InetAddress(ip,4,(unsigned int)port)) ? 1 : 0);
+		case AF_INET6:
+			return (reinterpret_cast<EthernetTap *>(tap)->removeIp(InetAddress(ip,16,(unsigned int)port)) ? 1 : 0);
+	}
+	return 0;
+}
+
+extern "C" int ZT_GoTap_ips(ZT_GoTap *tap,void *buf,unsigned int bufSize)
+{
+	auto ips = reinterpret_cast<EthernetTap *>(tap)->ips();
+	unsigned int p = 0;
+	uint8_t *const b = reinterpret_cast<uint8_t *>(buf);
+	for(auto ip=ips.begin();ip!=ips.end();++ip) {
+		if ((p + 7) > bufSize)
+			break;
+		const uint8_t *const ipd = reinterpret_cast<const uint8_t *>(ip->rawIpData());
+		const unsigned int port = ip->port();
+		if (ip->isV4()) {
+			b[p++] = AF_INET;
+			b[p++] = ipd[0];
+			b[p++] = ipd[1];
+			b[p++] = ipd[2];
+			b[p++] = ipd[3];
+			b[p++] = (uint8_t)((port >> 8) & 0xff);
+			b[p++] = (uint8_t)(port & 0xff);
+		} else if (ip->isV6()) {
+			if ((p + 19) <= bufSize) {
+				b[p++] = AF_INET6;
+				for(int j=0;j<16;++j)
+					b[p++] = ipd[j];
+				b[p++] = (uint8_t)((port >> 8) & 0xff);
+				b[p++] = (uint8_t)(port & 0xff);
+			}
+		}
+	}
+	return (int)p;
+}
+
+extern "C" void ZT_GoTap_deviceName(ZT_GoTap *tap,char nbuf[256])
+{
+	Utils::scopy(nbuf,256,reinterpret_cast<EthernetTap *>(tap)->deviceName().c_str());
+}
+
+extern "C" void ZT_GoTap_setFriendlyName(ZT_GoTap *tap,const char *friendlyName)
+{
+	reinterpret_cast<EthernetTap *>(tap)->setFriendlyName(friendlyName);
+}
+
+extern "C" void ZT_GoTap_setMtu(ZT_GoTap *tap,unsigned int mtu)
+{
+	reinterpret_cast<EthernetTap *>(tap)->setMtu(mtu);
+}

+ 5 - 59
go/native/GoGlue.h

@@ -31,57 +31,6 @@ typedef void ZT_GoTap;
 struct ZT_GoNode_Impl;
 typedef struct ZT_GoNode_Impl ZT_GoNode;
 
-#define ZT_GONODE_EVENT_SHUTDOWN 0
-#define ZT_GONODE_EVENT_ZTEVENT 1
-#define ZT_GONODE_EVENT_DNS_GET_TXT 2
-#define ZT_GONODE_EVENT_STATE_PUT 3
-#define ZT_GONODE_EVENT_STATE_DELETE 4
-#define ZT_GONODE_EVENT_NETWORK_CONFIG_UPDATE 5
-
-/**
- * Variant type for async core generated events pulled via waitForEvent
- */
-struct ZT_GoNodeEvent_Impl
-{
-#ifdef __cplusplus
-	inline ZT_GoNodeEvent_Impl() { memset(reinterpret_cast<void *>(this),0,sizeof(ZT_GoNodeEvent_Impl)); }
-	inline ZT_GoNodeEvent_Impl(const ZT_GoNodeEvent_Impl &ev) { memcpy(reinterpret_cast<void *>(this),reinterpret_cast<const void *>(&ev),sizeof(ZT_GoNodeEvent_Impl)); }
-	inline ZT_GoNodeEvent_Impl &operator=(const ZT_GoNodeEvent_Impl &ev) { memcpy(reinterpret_cast<void *>(this),reinterpret_cast<const void *>(&ev),sizeof(ZT_GoNodeEvent_Impl)); return *this; }
-#endif
-
-	int type;
-
-	union {
-		/* ZeroTier event of ZT_Event type */
-		struct {
-			int type;
-		} zt;
-
-		/* DNS resolution request */
-		struct {
-			uintptr_t requestId;
-			char dnsName[256];
-		} dns;
-
-		/* State object put or delete request */
-		struct {
-			uint8_t data[ZT_MAX_STATE_OBJECT_SIZE];
-			unsigned int len;
-			int objType;
-			uint64_t id[2];
-		} sobj;
-
-		/* Network configuration update event */
-		struct {
-			ZT_GoTap *tap;
-			int op; /* ZT_VirtualNetworkConfigOperation */
-			ZT_VirtualNetworkConfig conf;
-		} nconf;
-	} data;
-};
-
-typedef struct ZT_GoNodeEvent_Impl ZT_GoNodeEvent;
-
 /****************************************************************************/
 
 #ifdef __cplusplus
@@ -90,11 +39,10 @@ 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));
+
+/****************************************************************************/
+
+ZT_GoNode *ZT_GoNode_new(const char *workingPath);
 
 void ZT_GoNode_delete(ZT_GoNode *gn);
 
@@ -106,8 +54,6 @@ int ZT_GoNode_phyStartListen(ZT_GoNode *gn,const char *dev,const char *ip,const
 /* 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);
 
-void ZT_GoNode_waitForEvent(ZT_GoNode *gn,ZT_GoNodeEvent *ev);
-
 ZT_GoTap *ZT_GoNode_join(ZT_GoNode *gn,uint64_t nwid);
 
 void ZT_GoNode_leave(ZT_GoNode *gn,uint64_t nwid);
@@ -130,7 +76,7 @@ int ZT_GoTap_removeIp(ZT_GoTap *tap,int af,const void *ip,int port);
  */
 int ZT_GoTap_ips(ZT_GoTap *tap,void *buf,unsigned int bufSize);
 
-const char *ZT_GoTap_deviceName(ZT_GoTap *tap);
+void ZT_GoTap_deviceName(ZT_GoTap *tap,char nbuf[256]);
 
 void ZT_GoTap_setFriendlyName(ZT_GoTap *tap,const char *friendlyName);
 

+ 25 - 0
go/pkg/zerotier/errors.go

@@ -0,0 +1,25 @@
+/*
+ * Copyright (c)2019 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: 2023-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.
+ */
+/****/
+
+package zerotier
+
+// Err is a basic string error type for ZeroTier
+type Err string
+
+func (e Err) Error() string { return (string)(e) }
+
+// Simple ZeroTier Errors
+const (
+	ErrInvalidMACAddress Err = "invalid MAC address"
+	ErrInvalidParameter  Err = "invalid parameter"
+)

+ 63 - 0
go/pkg/zerotier/mac.go

@@ -0,0 +1,63 @@
+/*
+ * Copyright (c)2019 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: 2023-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.
+ */
+/****/
+
+package zerotier
+
+import (
+	"encoding/json"
+	"fmt"
+	"strconv"
+	"strings"
+)
+
+// MAC represents an Ethernet hardware address
+type MAC uint64
+
+// NewMACFromString decodes a MAC address in canonical colon-separated hex format
+func NewMACFromString(s string) (MAC, error) {
+	ss := strings.Split(s, ":")
+	if len(ss) != 6 {
+		return MAC(0), ErrInvalidMACAddress
+	}
+	var m uint64
+	for i := 0; i < 6; i++ {
+		m <<= 8
+		c, _ := strconv.ParseUint(ss[i], 16, 64)
+		if c > 0xff {
+			return MAC(0), ErrInvalidMACAddress
+		}
+		m |= (c & 0xff)
+	}
+	return MAC(m), nil
+}
+
+// String returns this MAC address in canonical human-readable form
+func (m MAC) String() string {
+	return fmt.Sprintf("%.2x:%.2x:%.2x:%.2x:%.2x:%.2x", (m>>40)&0xff, (m>>32)&0xff, (m>>24)&0xff, (m>>16)&0xff, (m>>8)&0xff, m&0xff)
+}
+
+// MarshalJSON marshals this MAC as a string
+func (m MAC) MarshalJSON() ([]byte, error) {
+	return []byte(m.String()), nil
+}
+
+// UnmarshalJSON unmarshals this MAC from a string
+func (m *MAC) UnmarshalJSON(j []byte) error {
+	var s string
+	err := json.Unmarshal(j, &s)
+	if err != nil {
+		return err
+	}
+	*m, err = NewMACFromString(s)
+	return err
+}

+ 1 - 1
go/pkg/ztnode/misc.go → go/pkg/zerotier/misc.go

@@ -11,7 +11,7 @@
  */
 /****/
 
-package ztnode
+package zerotier
 
 import "time"
 

+ 20 - 0
go/pkg/zerotier/multicastgroup.go

@@ -0,0 +1,20 @@
+/*
+ * Copyright (c)2019 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: 2023-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.
+ */
+/****/
+
+package zerotier
+
+// MulticastGroup represents a normal Ethernet multicast or broadcast address plus 32 additional ZeroTier-specific bits
+type MulticastGroup struct {
+	MAC MAC
+	ADI uint32
+}

+ 74 - 0
go/pkg/zerotier/network.go

@@ -0,0 +1,74 @@
+/*
+ * Copyright (c)2019 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: 2023-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.
+ */
+/****/
+
+package zerotier
+
+import (
+	"net"
+	"sync/atomic"
+	"time"
+)
+
+// NetworkConfig represents the network's current state
+type NetworkConfig struct {
+	// ID is this network's 64-bit globally unique identifier
+	ID uint64
+
+	// MAC is the Ethernet MAC address of this device on this network
+	MAC MAC
+
+	// Name is a short human-readable name set by the controller
+	Name string
+
+	// Status is a status code indicating this network's authorization status
+	Status int
+
+	// LastUpdated is the time this network's configuration was last updated from the controller
+	LastUpdated time.Time
+
+	// Type is this network's type
+	Type int
+
+	// MTU is the Ethernet MTU for this network
+	MTU int
+
+	// CanBridge is true if this network is allowed to bridge in other devices with different Ethernet addresses
+	CanBridge bool
+
+	// AllowsBroadcast is true if the broadcast (ff:ff:ff:ff:ff:ff) address works (excluding IPv4 ARP which is handled via a special path)
+	AllowsBroadcast bool
+
+	// IPs are static IPs assigned by the network controller to this device
+	IPs []net.IPNet
+
+	// Routes are static routes assigned by the network controller to this device
+	Routes []Route
+
+	// MulticastSubscriptions are this device's current multicast subscriptions
+	MulticastSubscriptions []MulticastGroup
+
+	// PortType is a human-readable description of this port's implementation type or name
+	PortType string
+
+	// PortDeviceName is the OS-specific device name (e.g. tun0 or feth1856) for this network's virtual port
+	PortDeviceName string
+
+	// PortErrorCode is an OS-specific error code returned by the virtual NIC driver
+	PortErrorCode int
+}
+
+// Network is a currently joined network
+type Network struct {
+	config atomic.Value
+	tap    atomic.Value
+}

+ 180 - 0
go/pkg/zerotier/node-callbacks.go

@@ -0,0 +1,180 @@
+/*
+ * Copyright (c)2019 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: 2023-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.
+ */
+/****/
+
+package zerotier
+
+//#cgo CFLAGS: -O3
+//#define ZT_CGO 1
+//#include <stdint.h>
+//#include <stdlib.h>
+//#include <string.h>
+//#include "../../native/GoGlue.h"
+import "C"
+
+import (
+	"net"
+	"sync"
+	"sync/atomic"
+	"unsafe"
+)
+
+const (
+	afInet  = C.AF_INET
+	afInet6 = C.AF_INET6
+
+	networkStatusRequestingConfiguration = C.ZT_NETWORK_STATUS_REQUESTING_CONFIGURATION
+	networkStatusOK                      = C.ZT_NETWORK_STATUS_OK
+	networkStatusAccessDenied            = C.ZT_NETWORK_STATUS_ACCESS_DENIED
+	networkStatusNotFound                = C.ZT_NETWORK_STATUS_NOT_FOUND
+	networkStatusPortError               = C.ZT_NETWORK_STATUS_PORT_ERROR
+	networkStatusClientTooOld            = C.ZT_NETWORK_STATUS_CLIENT_TOO_OLD
+)
+
+var (
+	nodesByUserPtr     map[uintptr]*Node
+	nodesByUserPtrLock sync.RWMutex
+)
+
+//export goPathCheckFunc
+func goPathCheckFunc(gn unsafe.Pointer, ztAddress C.uint64_t, af C.int, ip unsafe.Pointer, port C.int) C.int {
+	nodesByUserPtrLock.RLock()
+	node := nodesByUserPtr[uintptr(gn)]
+	nodesByUserPtrLock.RUnlock()
+	if node != nil && node.pathCheck(uint64(ztAddress), int(af), nil, int(port)) {
+		return 1
+	}
+	return 0
+}
+
+//export goPathLookupFunc
+func goPathLookupFunc(gn unsafe.Pointer, ztAddress C.uint64_t, desiredAddressFamily int, familyP, ipP, portP unsafe.Pointer) C.int {
+	nodesByUserPtrLock.RLock()
+	node := nodesByUserPtr[uintptr(gn)]
+	nodesByUserPtrLock.RUnlock()
+	if node == nil {
+		return 0
+	}
+
+	ip, port := node.pathLookup(uint64(ztAddress))
+	ip4 := ip.To4()
+	if len(ip4) == 4 {
+		*((*C.int)(familyP)) = afInet
+		copy((*[4]byte)(ipP)[:], ip4)
+		*((*C.int)(portP)) = C.int(port)
+	} else if len(ip) == 16 {
+		*((*C.int)(familyP)) = afInet6
+		copy((*[16]byte)(ipP)[:], ip)
+		*((*C.int)(portP)) = C.int(port)
+	}
+	return 0
+}
+
+//export goStateObjectPutFunc
+func goStateObjectPutFunc(gn unsafe.Pointer, objType C.int, id, data unsafe.Pointer, len C.int) {
+	nodesByUserPtrLock.RLock()
+	node := nodesByUserPtr[uintptr(gn)]
+	nodesByUserPtrLock.RUnlock()
+	if node == nil {
+		return
+	}
+	if len < 0 {
+		node.stateObjectDelete(int(objType), *((*[2]uint64)(id)))
+	} else {
+		node.stateObjectPut(int(objType), *((*[2]uint64)(id)), C.GoBytes(data, len))
+	}
+}
+
+//export goStateObjectGetFunc
+func goStateObjectGetFunc(gn unsafe.Pointer, objType C.int, id, data unsafe.Pointer, bufSize C.uint) C.int {
+	nodesByUserPtrLock.RLock()
+	node := nodesByUserPtr[uintptr(gn)]
+	nodesByUserPtrLock.RUnlock()
+	if node == nil {
+		return -1
+	}
+	tmp, found := node.stateObjectGet(int(objType), *((*[2]uint64)(id)))
+	if found && len(tmp) < int(bufSize) {
+		if len(tmp) > 0 {
+			C.memcpy(data, unsafe.Pointer(&(tmp[0])), C.ulong(len(tmp)))
+		}
+		return C.int(len(tmp))
+	}
+	return -1
+}
+
+//export goDNSResolverFunc
+func goDNSResolverFunc(gn unsafe.Pointer, dnsRecordTypes unsafe.Pointer, numDNSRecordTypes C.int, name unsafe.Pointer, requestID C.uintptr_t) {
+	nodesByUserPtrLock.RLock()
+	node := nodesByUserPtr[uintptr(gn)]
+	nodesByUserPtrLock.RUnlock()
+	if node == nil {
+		return
+	}
+
+	recordTypes := C.GoBytes(dnsRecordTypes, numDNSRecordTypes)
+	recordName := C.GoString((*C.char)(name))
+
+	go func() {
+		recordNameCStrCopy := C.CString(recordName)
+		for _, rt := range recordTypes {
+			switch rt {
+			case C.ZT_DNS_RECORD_TXT:
+				recs, _ := net.LookupTXT(recordName)
+				for _, rec := range recs {
+					if len(rec) > 0 {
+						rnCS := C.CString(rec)
+						C.ZT_Node_processDNSResult(unsafe.Pointer(node.zn), nil, requestID, recordNameCStrCopy, C.ZT_DNS_RECORD_TXT, unsafe.Pointer(rnCS), C.uint(len(rec)), 0)
+						C.free(unsafe.Pointer(rnCS))
+					}
+				}
+			}
+		}
+		C.ZT_Node_processDNSResult(unsafe.Pointer(node.zn), nil, requestID, recordNameCStrCopy, C.ZT_DNS_RECORD__END_OF_RESULTS, nil, 0, 0)
+		C.free(unsafe.Pointer(recordNameCStrCopy))
+	}()
+}
+
+//export goVirtualNetworkConfigFunc
+func goVirtualNetworkConfigFunc(gn, tapP unsafe.Pointer, nwid C.uint64_t, op C.int, conf unsafe.Pointer) C.int {
+	nodesByUserPtrLock.RLock()
+	node := nodesByUserPtr[uintptr(gn)]
+	nodesByUserPtrLock.RUnlock()
+	if node == nil {
+		return 255
+	}
+	return C.int(node.handleNetworkConfigUpdate(int(op), (*C.ZT_VirtualNetworkConfig)(conf)))
+}
+
+//export goZtEvent
+func goZtEvent(gn unsafe.Pointer, eventType C.int, data unsafe.Pointer) {
+	nodesByUserPtrLock.RLock()
+	node := nodesByUserPtr[uintptr(gn)]
+	nodesByUserPtrLock.RUnlock()
+	if node == nil {
+		return
+	}
+	switch eventType {
+	case C.ZT_EVENT_OFFLINE:
+		atomic.StoreUint32(&node.online, 0)
+	case C.ZT_EVENT_ONLINE:
+		atomic.StoreUint32(&node.online, 1)
+	case C.ZT_EVENT_TRACE:
+		node.handleTrace(C.GoString((*C.char)(data)))
+	case C.ZT_EVENT_USER_MESSAGE:
+		um := (*C.ZT_UserMessage)(data)
+		node.handleUserMessage(uint64(um.origin), uint64(um.typeId), C.GoBytes(um.data, C.int(um.length)))
+	case C.ZT_EVENT_REMOTE_TRACE:
+		rt := (*C.ZT_RemoteTrace)(data)
+		node.handleRemoteTrace(uint64(rt.origin), C.GoBytes(unsafe.Pointer(rt.data), C.int(rt.len)))
+	}
+}

+ 128 - 0
go/pkg/zerotier/node.go

@@ -0,0 +1,128 @@
+/*
+ * Copyright (c)2019 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: 2023-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.
+ */
+/****/
+
+package zerotier
+
+//#cgo CFLAGS: -O3
+//#cgo LDFLAGS: ${SRCDIR}/../../../build/node/libzt_core.a ${SRCDIR}/../../../build/go/native/libzt_go_native.a -lc++
+//#define ZT_CGO 1
+//#include <stdint.h>
+//#include "../../native/GoGlue.h"
+//#if __has_include("../../../version.h")
+//#include "../../../version.h"
+//#else
+//#define ZEROTIER_ONE_VERSION_MAJOR 255
+//#define ZEROTIER_ONE_VERSION_MINOR 255
+//#define ZEROTIER_ONE_VERSION_REVISION 255
+//#define ZEROTIER_ONE_VERSION_BUILD 255
+//#endif
+import "C"
+import (
+	"net"
+	"runtime"
+	"sync"
+	"sync/atomic"
+	"unsafe"
+)
+
+const (
+	// CoreVersionMajor is the major version of the ZeroTier core
+	CoreVersionMajor int = C.ZEROTIER_ONE_VERSION_MAJOR
+
+	// CoreVersionMinor is the minor version of the ZeroTier core
+	CoreVersionMinor int = C.ZEROTIER_ONE_VERSION_MINOR
+
+	// CoreVersionRevision is the revision of the ZeroTier core
+	CoreVersionRevision int = C.ZEROTIER_ONE_VERSION_REVISION
+
+	// CoreVersionBuild is the build version of the ZeroTier core
+	CoreVersionBuild int = C.ZEROTIER_ONE_VERSION_BUILD
+)
+
+// Tap is an instance of an EthernetTap object
+type Tap struct {
+	tap           *C.ZT_GoTap
+	networkStatus uint32
+}
+
+// Node is an instance of a ZeroTier node
+type Node struct {
+	gn *C.ZT_GoNode
+	zn *C.ZT_Node
+
+	taps     map[uint64]*Tap
+	tapsLock sync.RWMutex
+
+	online  uint32
+	running uint32
+}
+
+// NewNode creates and initializes a new instance of the ZeroTier node service
+func NewNode() *Node {
+	n := new(Node)
+
+	gnRawAddr := uintptr(unsafe.Pointer(n.gn))
+	nodesByUserPtrLock.Lock()
+	nodesByUserPtr[gnRawAddr] = n
+	nodesByUserPtrLock.Unlock()
+	runtime.SetFinalizer(n, func(obj interface{}) { // make sure this always happens
+		nodesByUserPtrLock.Lock()
+		delete(nodesByUserPtr, gnRawAddr)
+		nodesByUserPtrLock.Unlock()
+	})
+
+	n.running = 1
+
+	return n
+}
+
+// Close closes this Node and frees its underlying C++ Node structures
+func (n *Node) Close() {
+	if atomic.SwapUint32(&n.running, 0) != 0 {
+		C.ZT_GoNode_delete(n.gn)
+		nodesByUserPtrLock.Lock()
+		delete(nodesByUserPtr, uintptr(unsafe.Pointer(n.gn)))
+		nodesByUserPtrLock.Unlock()
+	}
+}
+
+func (n *Node) pathCheck(ztAddress uint64, af int, ip net.IP, port int) bool {
+	return true
+}
+
+func (n *Node) pathLookup(ztAddress uint64) (net.IP, int) {
+	return nil, 0
+}
+
+func (n *Node) stateObjectPut(objType int, id [2]uint64, data []byte) {
+}
+
+func (n *Node) stateObjectDelete(objType int, id [2]uint64) {
+}
+
+func (n *Node) stateObjectGet(objType int, id [2]uint64) ([]byte, bool) {
+	return nil, false
+}
+
+func (n *Node) handleTrace(traceMessage string) {
+}
+
+func (n *Node) handleUserMessage(originAddress, messageTypeID uint64, data []byte) {
+}
+
+func (n *Node) handleRemoteTrace(originAddress uint64, dictData []byte) {
+}
+
+func (n *Node) handleNetworkConfigUpdate(op int, config *C.ZT_VirtualNetworkConfig) int {
+	return 0
+}

+ 28 - 0
go/pkg/zerotier/route.go

@@ -0,0 +1,28 @@
+/*
+ * Copyright (c)2019 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: 2023-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.
+ */
+/****/
+
+package zerotier
+
+import "net"
+
+// Route represents a route in a host's routing table
+type Route struct {
+	// Target for this route
+	Target net.IPNet
+
+	// Via is how to reach this target (null/empty if the target IP range is local to this virtual LAN)
+	Via net.IP
+
+	// Metric is an interface metric that can affect route priority (behavior can be OS-specific)
+	Metric int
+}

+ 0 - 122
go/pkg/ztnode/errors.go

@@ -1,122 +0,0 @@
-/*
- * Copyright (c)2019 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: 2023-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.
- */
-/****/
-
-package ztnode
-
-/*
-
-// errTypeName returns the type name of an error minus any leading * character.
-func errTypeName(err error) string {
-	if err == nil {
-		return ""
-	}
-	et := reflect.TypeOf(err)
-	if et.Kind() == reflect.Ptr {
-		return et.Elem().Name()
-	}
-	return et.Name()
-}
-
-//////////////////////////////////////////////////////////////////////////////
-
-// Err indicates a general LF error such as an invalid parameter or state.
-type Err string
-
-func (e Err) Error() string { return (string)(e) }
-
-// General errors
-const (
-	ErrInvalidPublicKey       Err = "invalid public key"
-	ErrInvalidPrivateKey      Err = "invalid private key"
-	ErrInvalidParameter       Err = "invalid parameter"
-	ErrInvalidObject          Err = "invalid object"
-	ErrUnsupportedType        Err = "unsupported type"
-	ErrUnsupportedCurve       Err = "unsupported ECC curve (for this purpose)"
-	ErrOutOfRange             Err = "parameter out of range"
-	ErrWharrgarblFailed       Err = "Wharrgarbl proof of work algorithm failed (out of memory?)"
-	ErrIO                     Err = "I/O error"
-	ErrIncorrectKey           Err = "incorrect key"
-	ErrAlreadyConnected       Err = "already connected"
-	ErrRecordNotFound         Err = "record not found"
-	ErrRecordIsNewer          Err = "record is newer than timestamp"
-	ErrPulseSpanExeceeded     Err = "pulse is more than one year after record"
-	ErrDuplicateRecord        Err = "duplicate record"
-	ErrPrivateKeyRequired     Err = "private key required"
-	ErrInvalidMessageSize     Err = "message size invalid"
-	ErrQueryRequiresSelectors Err = "query requires at least one selector"
-	ErrQueryInvalidSortOrder  Err = "invalid sort order value"
-	ErrAlreadyMounted         Err = "mount point already mounted"
-)
-
-//////////////////////////////////////////////////////////////////////////////
-
-// ErrRecord indicates an error related to an invalid record or a record failing a check.
-type ErrRecord string
-
-func (e ErrRecord) Error() string { return (string)(e) }
-
-// Errs indicating that a record is invalid
-const (
-	ErrRecordInvalid                   ErrRecord = "record invalid"
-	ErrRecordOwnerSignatureCheckFailed ErrRecord = "owner signature check failed"
-	ErrRecordInsufficientWork          ErrRecord = "insufficient work to pay for this record"
-	ErrRecordNotApproved               ErrRecord = "record not currently approved (via proof of work and/or certificates)"
-	ErrRecordInsufficientLinks         ErrRecord = "insufficient links"
-	ErrRecordTooManyLinks              ErrRecord = "too many links"
-	ErrRecordInvalidLinks              ErrRecord = "links must be sorted and unique"
-	ErrRecordTooManySelectors          ErrRecord = "too many selectors"
-	ErrRecordUnsupportedAlgorithm      ErrRecord = "unsupported algorithm or type"
-	ErrRecordTooLarge                  ErrRecord = "record too large"
-	ErrRecordValueTooLarge             ErrRecord = "record value too large"
-	ErrRecordViolatesSpecialRelativity ErrRecord = "record timestamp too far in the future"
-	ErrRecordTooOld                    ErrRecord = "record older than network timestamp floor"
-	ErrRecordCertificateInvalid        ErrRecord = "certificate invalid"
-	ErrRecordCertificateRequired       ErrRecord = "certificate required"
-	ErrRecordProhibited                ErrRecord = "record administratively prohibited"
-)
-
-//////////////////////////////////////////////////////////////////////////////
-
-// ErrDatabase contains information about a database related problem.
-type ErrDatabase struct {
-	// ErrCode is the error code returned by the C database module.
-	ErrCode int
-
-	// ErrMessage is an error message supplied by the C code or by Go (optional)
-	ErrMessage string
-}
-
-func (e ErrDatabase) Error() string {
-	return fmt.Sprintf("database error: %d (%s)", e.ErrCode, e.ErrMessage)
-}
-
-//////////////////////////////////////////////////////////////////////////////
-
-// ErrAPI (response) indicates an error and is returned with non-200 responses.
-type ErrAPI struct {
-	Code        int    ``                  // HTTP response code
-	Message     string `json:",omitempty"` // Message indicating the reason for the error
-	ErrTypeName string `json:",omitempty"` // Name of LF native error or empty if HTTP or transport error
-}
-
-// Error implements the error interface, making APIError an 'error' in the Go sense.
-func (e ErrAPI) Error() string {
-	if len(e.ErrTypeName) > 0 {
-		return fmt.Sprintf("%d:%s:%s", e.Code, e.ErrTypeName, e.Message)
-	}
-	return fmt.Sprintf("%d:%s", e.Code, e.Message)
-}
-
-//////////////////////////////////////////////////////////////////////////////
-
-*/

+ 0 - 55
go/pkg/ztnode/node.go

@@ -1,55 +0,0 @@
-/*
- * Copyright (c)2019 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: 2023-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.
- */
-/****/
-
-package ztnode
-
-//#cgo CFLAGS: -O3
-//#cgo LDFLAGS: ${SRCDIR}/../../../build/node/libzt_core.a ${SRCDIR}/../../../build/go/native/libzt_go_native.a -lc++
-//#define ZT_CGO 1
-//#include <stdint.h>
-//#include "../../native/GoGlue.h"
-//#if __has_include("../../../version.h")
-//#include "../../../version.h"
-//#else
-//#define ZEROTIER_ONE_VERSION_MAJOR 255
-//#define ZEROTIER_ONE_VERSION_MINOR 255
-//#define ZEROTIER_ONE_VERSION_REVISION 255
-//#define ZEROTIER_ONE_VERSION_BUILD 255
-//#endif
-import "C"
-import "sync"
-
-const (
-	// CoreVersionMajor is the major version of the ZeroTier core
-	CoreVersionMajor int = C.ZEROTIER_ONE_VERSION_MAJOR
-
-	// CoreVersionMinor is the minor version of the ZeroTier core
-	CoreVersionMinor int = C.ZEROTIER_ONE_VERSION_MINOR
-
-	// CoreVersionRevision is the revision of the ZeroTier core
-	CoreVersionRevision int = C.ZEROTIER_ONE_VERSION_REVISION
-
-	// CoreVersionBuild is the build version of the ZeroTier core
-	CoreVersionBuild int = C.ZEROTIER_ONE_VERSION_BUILD
-)
-
-// Node is an instance of a ZeroTier node
-type Node struct {
-	gn *C.ZT_GoNode
-	zn *C.ZT_Node
-}
-
-var (
-	nodesByUserPtr     map[uintptr]*Node
-	nodesByUserPtrLock sync.Mutex
-)

+ 1 - 1
include/ZeroTierCore.h

@@ -983,7 +983,7 @@ typedef struct
 	uint16_t flags;
 
 	/**
-	 * Route metric (not currently used)
+	 * Route metric
 	 */
 	uint16_t metric;
 } ZT_VirtualNetworkRoute;