This commit is contained in:
Adam Ierymenko 2019-09-20 19:51:57 -07:00
parent b34aa10bf8
commit 02a6b15e6b
No known key found for this signature in database
GPG Key ID: C8877CF2D7A5D7F3
9 changed files with 297 additions and 40 deletions

View File

@ -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();
}
}
}

View File

@ -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.

View File

@ -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
}

View File

@ -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"
)

View File

@ -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

View File

@ -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 ""
}

View File

@ -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
}

View File

@ -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
go/pkg/zerotier/tap.go Normal file
View File

@ -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
}