Adam Ierymenko 5 years ago
parent
commit
02a6b15e6b

+ 10 - 13
go/native/GoGlue.cpp

@@ -575,24 +575,24 @@ 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)
+extern "C" int ZT_GoTap_addIp(ZT_GoTap *tap,int af,const void *ip,int netmaskBits)
 {
 	switch(af) {
 		case AF_INET:
-			return (reinterpret_cast<EthernetTap *>(tap)->addIp(InetAddress(ip,4,(unsigned int)port)) ? 1 : 0);
+			return (reinterpret_cast<EthernetTap *>(tap)->addIp(InetAddress(ip,4,(unsigned int)netmaskBits)) ? 1 : 0);
 		case AF_INET6:
-			return (reinterpret_cast<EthernetTap *>(tap)->addIp(InetAddress(ip,16,(unsigned int)port)) ? 1 : 0);
+			return (reinterpret_cast<EthernetTap *>(tap)->addIp(InetAddress(ip,16,(unsigned int)netmaskBits)) ? 1 : 0);
 	}
 	return 0;
 }
 
-extern "C" int ZT_GoTap_removeIp(ZT_GoTap *tap,int af,const void *ip,int port)
+extern "C" int ZT_GoTap_removeIp(ZT_GoTap *tap,int af,const void *ip,int netmaskBits)
 {
 	switch(af) {
 		case AF_INET:
-			return (reinterpret_cast<EthernetTap *>(tap)->removeIp(InetAddress(ip,4,(unsigned int)port)) ? 1 : 0);
+			return (reinterpret_cast<EthernetTap *>(tap)->removeIp(InetAddress(ip,4,(unsigned int)netmaskBits)) ? 1 : 0);
 		case AF_INET6:
-			return (reinterpret_cast<EthernetTap *>(tap)->removeIp(InetAddress(ip,16,(unsigned int)port)) ? 1 : 0);
+			return (reinterpret_cast<EthernetTap *>(tap)->removeIp(InetAddress(ip,16,(unsigned int)netmaskBits)) ? 1 : 0);
 	}
 	return 0;
 }
@@ -603,25 +603,22 @@ extern "C" int ZT_GoTap_ips(ZT_GoTap *tap,void *buf,unsigned int bufSize)
 	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)
+		if ((p + 6) > 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);
+			b[p++] = (uint8_t)ip->netmaskBits();
 		} else if (ip->isV6()) {
-			if ((p + 19) <= bufSize) {
+			if ((p + 18) <= 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);
+				b[p++] = (uint8_t)ip->netmaskBits();
 			}
 		}
 	}

+ 3 - 3
go/native/GoGlue.h

@@ -62,14 +62,14 @@ 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_addIp(ZT_GoTap *tap,int af,const void *ip,int netmaskBits);
 
-int ZT_GoTap_removeIp(ZT_GoTap *tap,int af,const void *ip,int port);
+int ZT_GoTap_removeIp(ZT_GoTap *tap,int af,const void *ip,int netmaskBits);
 
 /* The buf buffer is filled with tuplies of:
  *   uint8_t family
  *   uint8_t ip[4 or 16]
- *   uint16_t port (big-endian byte order)
+ *   uint8_t netmask bits (up to 32 for ipv4, 128 for ipv6)
  *
  * This function returns the number of such tuples in the result.
  * If the buffer isn't big enough results are incomplete.

+ 54 - 0
go/pkg/zerotier/address.go

@@ -0,0 +1,54 @@
+/*
+ * 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"
+)
+
+// Address represents a 40-bit short ZeroTier address
+type Address uint64
+
+// NewAddressFromString parses a 10-digit ZeroTier address
+func NewAddressFromString(s string) (Address, error) {
+	if len(s) != 10 {
+		return Address(0), ErrInvalidZeroTierAddress
+	}
+	a, err := strconv.ParseUint(s, 16, 64)
+	return Address(a & 0xffffffffff), err
+}
+
+// String returns this address's 10-digit hex identifier
+func (a Address) String() string {
+	return fmt.Sprintf("%.10x", uint64(a))
+}
+
+// MarshalJSON marshals this Address as a string
+func (a Address) MarshalJSON() ([]byte, error) {
+	return []byte(a.String()), nil
+}
+
+// UnmarshalJSON unmarshals this Address from a string
+func (a *Address) UnmarshalJSON(j []byte) error {
+	var s string
+	err := json.Unmarshal(j, &s)
+	if err != nil {
+		return err
+	}
+	tmp, err := NewAddressFromString(s)
+	*a = tmp
+	return err
+}

+ 3 - 2
go/pkg/zerotier/errors.go

@@ -20,6 +20,7 @@ func (e Err) Error() string { return (string)(e) }
 
 // Simple ZeroTier Errors
 const (
-	ErrInvalidMACAddress Err = "invalid MAC address"
-	ErrInvalidParameter  Err = "invalid parameter"
+	ErrInvalidMACAddress      Err = "invalid MAC address"
+	ErrInvalidZeroTierAddress Err = "invalid ZeroTier address"
+	ErrInvalidParameter       Err = "invalid parameter"
 )

+ 1 - 1
go/pkg/zerotier/mac.go

@@ -43,7 +43,7 @@ func NewMACFromString(s string) (MAC, error) {
 
 // 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)
+	return fmt.Sprintf("%.2x:%.2x:%.2x:%.2x:%.2x:%.2x", (uint64(m)>>40)&0xff, (uint64(m)>>32)&0xff, (uint64(m)>>24)&0xff, (uint64(m)>>16)&0xff, (uint64(m)>>8)&0xff, uint64(m)&0xff)
 }
 
 // MarshalJSON marshals this MAC as a string

+ 137 - 0
go/pkg/zerotier/nativetap.go

@@ -0,0 +1,137 @@
+/*
+ * 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 (
+	"fmt"
+	"net"
+	"sync/atomic"
+	"unsafe"
+)
+
+//#cgo CFLAGS: -O3
+//#define ZT_CGO 1
+//#include <stdint.h>
+//#include <stdlib.h>
+//#include <string.h>
+//#include "../../native/GoGlue.h"
+import "C"
+
+// nativeTap is a Tap implementation that wraps a native C++ interface to a system tun/tap device
+type nativeTap struct {
+	tap           unsafe.Pointer
+	networkStatus uint32
+	enabled       uint32
+}
+
+// SetEnabled sets this tap's enabled state
+func (t *nativeTap) SetEnabled(enabled bool) {
+	if enabled && atomic.SwapUint32(&t.enabled, 1) == 0 {
+		C.ZT_GoTap_setEnabled(t.tap, 1)
+	} else if !enabled && atomic.SwapUint32(&t.enabled, 0) == 1 {
+		C.ZT_GoTap_setEnabled(t.tap, 0)
+	}
+}
+
+// Enabled returns true if this tap is currently processing packets
+func (t *nativeTap) Enabled() bool {
+	return atomic.LoadUint32(&t.enabled) != 0
+}
+
+// AddIP adds an IP address (with netmask) to this tap
+func (t *nativeTap) AddIP(ip net.IPNet) error {
+	bits, _ := ip.Mask.Size()
+	if len(ip.IP) == 16 {
+		if bits > 128 || bits < 0 {
+			return ErrInvalidParameter
+		}
+		C.ZT_GoTap_addIp(t.tap, afInet6, unsafe.Pointer(&ip.IP[0]), C.int(bits))
+	} else if len(ip.IP) == 4 {
+		if bits > 32 || bits < 0 {
+			return ErrInvalidParameter
+		}
+		C.ZT_GoTap_addIp(t.tap, afInet, unsafe.Pointer(&ip.IP[0]), C.int(bits))
+	}
+	return ErrInvalidParameter
+}
+
+// RemoveIP removes this IP address (with netmask) from this tap
+func (t *nativeTap) RemoveIP(ip net.IPNet) error {
+	bits, _ := ip.Mask.Size()
+	if len(ip.IP) == 16 {
+		if bits > 128 || bits < 0 {
+			return ErrInvalidParameter
+		}
+		C.ZT_GoTap_removeIp(t.tap, afInet6, unsafe.Pointer(&ip.IP[0]), C.int(bits))
+		return nil
+	}
+	if len(ip.IP) == 4 {
+		if bits > 32 || bits < 0 {
+			return ErrInvalidParameter
+		}
+		C.ZT_GoTap_removeIp(t.tap, afInet, unsafe.Pointer(&ip.IP[0]), C.int(bits))
+		return nil
+	}
+	return ErrInvalidParameter
+}
+
+// IPs returns IPs currently assigned to this tap (including externally or system-assigned IPs)
+func (t *nativeTap) IPs() (ips []net.IPNet, err error) {
+	defer func() {
+		e := recover()
+		if e != nil {
+			err = fmt.Errorf("%v", e)
+		}
+	}()
+	var ipbuf [16384]byte
+	count := int(C.ZT_GoTap_ips(t.tap, unsafe.Pointer(&ipbuf[0]), 16384))
+	ipptr := 0
+	for i := 0; i < count; i++ {
+		af := ipbuf[ipptr]
+		ipptr++
+		switch af {
+		case afInet:
+			var ip [4]byte
+			for j := 0; j < 4; j++ {
+				ip[j] = ipbuf[ipptr]
+				ipptr++
+			}
+			bits := ipbuf[ipptr]
+			ipptr++
+			ips = append(ips, net.IPNet{IP: net.IP(ip[:]), Mask: net.CIDRMask(int(bits), 32)})
+		case afInet6:
+			var ip [16]byte
+			for j := 0; j < 16; j++ {
+				ip[j] = ipbuf[ipptr]
+				ipptr++
+			}
+			bits := ipbuf[ipptr]
+			ipptr++
+			ips = append(ips, net.IPNet{IP: net.IP(ip[:]), Mask: net.CIDRMask(int(bits), 128)})
+		}
+	}
+	return
+}
+
+// DeviceName gets this tap's OS-specific device name
+func (t *nativeTap) DeviceName() string {
+	var dn [256]byte
+	C.ZT_GoTap_deviceName(t.tap, (*C.char)(unsafe.Pointer(&dn[0])))
+	for i, b := range dn {
+		if b == 0 {
+			return string(dn[0:i])
+		}
+	}
+	return ""
+}

+ 52 - 6
go/pkg/zerotier/network.go

@@ -14,15 +14,52 @@
 package zerotier
 
 import (
+	"encoding/json"
+	"fmt"
 	"net"
-	"sync/atomic"
+	"strconv"
+	"sync"
 	"time"
 )
 
+// NetworkID is a network's 64-bit unique ID
+type NetworkID uint64
+
+// NewNetworkIDFromString parses a network ID in string form
+func NewNetworkIDFromString(s string) (NetworkID, error) {
+	if len(s) != 16 {
+		return NetworkID(0), ErrInvalidZeroTierAddress
+	}
+	n, err := strconv.ParseUint(s, 16, 64)
+	return NetworkID(n), err
+}
+
+// String returns this network ID's 16-digit hex identifier
+func (n NetworkID) String() string {
+	return fmt.Sprintf("%.16x", uint64(n))
+}
+
+// MarshalJSON marshals this NetworkID as a string
+func (n NetworkID) MarshalJSON() ([]byte, error) {
+	return []byte(n.String()), nil
+}
+
+// UnmarshalJSON unmarshals this NetworkID from a string
+func (n *NetworkID) UnmarshalJSON(j []byte) error {
+	var s string
+	err := json.Unmarshal(j, &s)
+	if err != nil {
+		return err
+	}
+	tmp, err := NewNetworkIDFromString(s)
+	*n = tmp
+	return err
+}
+
 // NetworkConfig represents the network's current state
 type NetworkConfig struct {
 	// ID is this network's 64-bit globally unique identifier
-	ID uint64
+	ID NetworkID
 
 	// MAC is the Ethernet MAC address of this device on this network
 	MAC MAC
@@ -57,8 +94,8 @@ type NetworkConfig struct {
 	// 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
+	// PortDeviceType is a human-readable description of this port's implementation type or name
+	PortDeviceType string
 
 	// PortDeviceName is the OS-specific device name (e.g. tun0 or feth1856) for this network's virtual port
 	PortDeviceName string
@@ -69,6 +106,15 @@ type NetworkConfig struct {
 
 // Network is a currently joined network
 type Network struct {
-	config atomic.Value
-	tap    atomic.Value
+	config     NetworkConfig
+	configLock sync.RWMutex
+	tap        *Tap
+	tapLock    sync.Mutex
+}
+
+// Config returns a copy of this network's current configuration
+func (n *Network) Config() NetworkConfig {
+	n.configLock.RLock()
+	defer n.configLock.RUnlock()
+	return n.config
 }

+ 11 - 15
go/pkg/zerotier/node.go

@@ -13,6 +13,14 @@
 
 package zerotier
 
+import (
+	"net"
+	"runtime"
+	"sync"
+	"sync/atomic"
+	"unsafe"
+)
+
 //#cgo CFLAGS: -O3
 //#cgo LDFLAGS: ${SRCDIR}/../../../build/node/libzt_core.a ${SRCDIR}/../../../build/go/native/libzt_go_native.a -lc++
 //#define ZT_CGO 1
@@ -27,13 +35,6 @@ package zerotier
 //#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
@@ -49,19 +50,13 @@ const (
 	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
+	networks     map[uint64]*Network
+	networksLock sync.RWMutex
 
 	online  uint32
 	running uint32
@@ -70,6 +65,7 @@ type Node struct {
 // NewNode creates and initializes a new instance of the ZeroTier node service
 func NewNode() *Node {
 	n := new(Node)
+	n.networks = make(map[uint64]*Network)
 
 	gnRawAddr := uintptr(unsafe.Pointer(n.gn))
 	nodesByUserPtrLock.Lock()

+ 26 - 0
go/pkg/zerotier/tap.go

@@ -0,0 +1,26 @@
+/*
+ * 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"
+
+// Tap represents an Ethernet tap connecting a virtual network to a device or something else "real"
+type Tap interface {
+	SetEnabled(enabled bool)
+	Enabled() bool
+	AddIP(ip net.IPNet) error
+	RemoveIP(ip net.IPNet) error
+	IPs() ([]net.IPNet, error)
+	DeviceName() string
+}