diff --git a/go/native/GoGlue.cpp b/go/native/GoGlue.cpp index 3f12fbf3..f2fe1ed6 100644 --- a/go/native/GoGlue.cpp +++ b/go/native/GoGlue.cpp @@ -336,14 +336,16 @@ extern "C" ZT_GoNode *ZT_GoNode_new(const char *workingPath) gn->run = true; gn->backgroundTaskThread = std::thread([gn] { - int64_t lastScannedMulticastGroups = 0; + int64_t lastCheckedTaps = 0; while (gn->run) { std::this_thread::sleep_for(std::chrono::milliseconds(500)); const int64_t now = OSUtils::now(); + if (now >= gn->nextBackgroundTaskDeadline) gn->node->processBackgroundTasks(nullptr,now,&(gn->nextBackgroundTaskDeadline)); - if ((now - lastScannedMulticastGroups) > 5000) { - lastScannedMulticastGroups = now; + + if ((now - lastCheckedTaps) > 10000) { + lastCheckedTaps = now; std::vector added,removed; std::lock_guard tl(gn->taps_l); for(auto t=gn->taps.begin();t!=gn->taps.end();++t) { @@ -354,6 +356,8 @@ extern "C" ZT_GoNode *ZT_GoNode_new(const char *workingPath) goHandleTapAddedMulticastGroup(gn,(ZT_GoTap *)t->second.get(),t->first,g->mac().toInt(),g->adi()); for(auto g=removed.begin();g!=removed.end();++g) goHandleTapRemovedMulticastGroup(gn,(ZT_GoTap *)t->second.get(),t->first,g->mac().toInt(),g->adi()); + + t->second->syncRoutes(); } } } @@ -705,8 +709,3 @@ extern "C" int ZT_GoTap_removeRoute(ZT_GoTap *tap,int targetAf,const void *targe } return reinterpret_cast(tap)->removeRoute(target,via,metric); } - -extern "C" int ZT_GoTap_syncRoutes(ZT_GoTap *tap) -{ - return reinterpret_cast(tap)->syncRoutes(); -} diff --git a/go/native/GoGlue.h b/go/native/GoGlue.h index f6b0c58b..25ce1d7f 100644 --- a/go/native/GoGlue.h +++ b/go/native/GoGlue.h @@ -95,8 +95,6 @@ int ZT_GoTap_addRoute(ZT_GoTap *tap,int targetAf,const void *targetIp,int target int ZT_GoTap_removeRoute(ZT_GoTap *tap,int targetAf,const void *targetIp,int targetNetmaskBits,int viaAf,const void *viaIp,unsigned int metric); -int ZT_GoTap_syncRoutes(ZT_GoTap *tap); - /****************************************************************************/ #ifdef __cplusplus diff --git a/go/pkg/zerotier/address.go b/go/pkg/zerotier/address.go index bd0f1bf7..5131bf29 100644 --- a/go/pkg/zerotier/address.go +++ b/go/pkg/zerotier/address.go @@ -31,6 +31,17 @@ func NewAddressFromString(s string) (Address, error) { return Address(a & 0xffffffffff), err } +// NewAddressFromBytes reads a 5-byte 40-bit address. +func NewAddressFromBytes(b []byte) (Address, error) { + if len(b) < 5 { + return Address(0), ErrInvalidZeroTierAddress + } + return Address((uint64(b[0]) << 32) | (uint64(b[1]) << 24) | (uint64(b[2]) << 16) | (uint64(b[3]) << 8) | uint64(b[4])), nil +} + +// IsReserved returns true if this address is reserved and therefore is not valid for a real node. +func (a Address) IsReserved() bool { return a == 0 || (a>>32) == 0xff } + // String returns this address's 10-digit hex identifier func (a Address) String() string { return fmt.Sprintf("%.10x", uint64(a)) diff --git a/go/pkg/zerotier/errors.go b/go/pkg/zerotier/errors.go index 54cf7608..da858a39 100644 --- a/go/pkg/zerotier/errors.go +++ b/go/pkg/zerotier/errors.go @@ -23,6 +23,7 @@ const ( ErrNodeInitFailed Err = "unable to initialize core Node instance" ErrInvalidMACAddress Err = "invalid MAC address" ErrInvalidZeroTierAddress Err = "invalid ZeroTier address" + ErrInvalidNetworkID Err = "invalid network ID" ErrInvalidParameter Err = "invalid parameter" ErrTapInitFailed Err = "unable to create native Tap instance" ErrUncrecognizedIdentityType Err = "unrecognized identity type" diff --git a/go/pkg/zerotier/identity.go b/go/pkg/zerotier/identity.go index f6e729a2..67aa0e1f 100644 --- a/go/pkg/zerotier/identity.go +++ b/go/pkg/zerotier/identity.go @@ -123,9 +123,15 @@ func (id *Identity) PrivateKeyString() string { // PublicKeyString returns the address and public key (identity.public contents). // An empty string is returned if this identity is invalid or not initialized. func (id *Identity) String() string { - if len(id.publicKey) == IdentityTypeC25519PublicKeySize { - s := fmt.Sprintf("%.10x:0:%x", id.address, id.publicKey) - return s + switch id.idtype { + case IdentityTypeC25519: + if len(id.publicKey) == IdentityTypeC25519PublicKeySize { + return fmt.Sprintf("%.10x:0:%x", id.address, id.publicKey) + } + case IdentityTypeP384: + if len(id.publicKey) == IdentityTypeP384PublicKeySize { + return fmt.Sprintf("%.10x:1:%s", uint64(id.address), base32StdLowerCase.EncodeToString(id.publicKey)) + } } return "" } diff --git a/go/pkg/zerotier/misc.go b/go/pkg/zerotier/misc.go index 0af65c2b..4a2861ff 100644 --- a/go/pkg/zerotier/misc.go +++ b/go/pkg/zerotier/misc.go @@ -21,7 +21,7 @@ import ( "unsafe" ) -var base32StdLowerCase = base32.NewEncoding("abcdefghijklmnopqrstuvwxyz234567") +var base32StdLowerCase = base32.NewEncoding("abcdefghijklmnopqrstuvwxyz234567").WithPadding(base32.NoPadding) // TimeMs returns the time in milliseconds since epoch. func TimeMs() int64 { return int64(time.Now().UnixNano()) / int64(1000000) } diff --git a/go/pkg/zerotier/network.go b/go/pkg/zerotier/network.go index faefb5e6..a6712a22 100644 --- a/go/pkg/zerotier/network.go +++ b/go/pkg/zerotier/network.go @@ -14,6 +14,7 @@ package zerotier import ( + "encoding/binary" "encoding/json" "fmt" "net" @@ -28,17 +29,32 @@ 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 + return NetworkID(0), ErrInvalidNetworkID } n, err := strconv.ParseUint(s, 16, 64) return NetworkID(n), err } +// NewNetworkIDFromBytes reads an 8-byte / 64-bit network ID. +func NewNetworkIDFromBytes(b []byte) (NetworkID, error) { + if len(b) < 8 { + return NetworkID(0), ErrInvalidNetworkID + } + return NetworkID(binary.BigEndian.Uint64(b)), nil +} + // String returns this network ID's 16-digit hex identifier func (n NetworkID) String() string { return fmt.Sprintf("%.16x", uint64(n)) } +// Bytes returns this network ID as an 8-byte / 64-bit big-endian value. +func (n NetworkID) Bytes() []byte { + var b [8]byte + binary.BigEndian.PutUint64(b[:], uint64(n)) + return b[:] +} + // MarshalJSON marshals this NetworkID as a string func (n NetworkID) MarshalJSON() ([]byte, error) { return []byte("\"" + n.String() + "\""), nil @@ -56,7 +72,7 @@ func (n *NetworkID) UnmarshalJSON(j []byte) error { return err } -// NetworkConfig represents the network's current state +// NetworkConfig represents the network's current configuration as distributed by its network controller. type NetworkConfig struct { // ID is this network's 64-bit globally unique identifier ID NetworkID diff --git a/go/pkg/zerotier/node.go b/go/pkg/zerotier/node.go index 0bf869b0..473262f9 100644 --- a/go/pkg/zerotier/node.go +++ b/go/pkg/zerotier/node.go @@ -266,9 +266,9 @@ func (n *Node) RemoveDynamicRoot(dnsName string) { C.free(unsafe.Pointer(dn)) } -// ListRoots retrieves a list of root servers on this node and their preferred and online status. -func (n *Node) ListRoots() []Root { - var roots []Root +// Roots retrieves a list of root servers on this node and their preferred and online status. +func (n *Node) Roots() []*Root { + var roots []*Root rl := C.ZT_Node_listRoots(unsafe.Pointer(n.zn), C.int64_t(TimeMs())) if rl != nil { for i := 0; i < int(rl.count); i++ { @@ -282,7 +282,7 @@ func (n *Node) ListRoots() []Root { addrs = append(addrs, a) } } - roots = append(roots, Root{ + roots = append(roots, &Root{ DNSName: C.GoString(root.dnsName), Identity: id, Addresses: addrs, @@ -291,11 +291,53 @@ func (n *Node) ListRoots() []Root { }) } } - defer C.ZT_Node_freeQueryResult(unsafe.Pointer(n.zn), unsafe.Pointer(rl)) + C.ZT_Node_freeQueryResult(unsafe.Pointer(n.zn), unsafe.Pointer(rl)) } return roots } +// Peers retrieves a list of current peers +func (n *Node) Peers() []*Peer { + var peers []*Peer + pl := C.ZT_Node_peers(unsafe.Pointer(n.zn)) + if pl != nil { + for i := uintptr(0); i < uintptr(pl.peerCount); i++ { + p := (*C.ZT_Peer)(unsafe.Pointer(uintptr(unsafe.Pointer(pl.peers)) + (i * C.sizeof_ZT_Peer))) + p2 := new(Peer) + p2.Address = Address(p.address) + p2.Version = [3]int{int(p.versionMajor), int(p.versionMinor), int(p.versionRev)} + p2.Latency = int(p.latency) + p2.Role = int(p.role) + p2.Paths = make([]Path, 0, int(p.pathCount)) + for j := uintptr(0); j < uintptr(p.pathCount); j++ { + pt := &p.paths[j] + a := sockaddrStorageToUDPAddr(&pt.address) + if a != nil { + p2.Paths = append(p2.Paths, Path{ + IP: a.IP, + Port: a.Port, + LastSend: int64(pt.lastSend), + LastReceive: int64(pt.lastReceive), + TrustedPathID: uint64(pt.trustedPathId), + Latency: float32(pt.latency), + PacketDelayVariance: float32(pt.packetDelayVariance), + ThroughputDisturbCoeff: float32(pt.throughputDisturbCoeff), + PacketErrorRatio: float32(pt.packetErrorRatio), + PacketLossRatio: float32(pt.packetLossRatio), + Stability: float32(pt.stability), + Throughput: uint64(pt.throughput), + MaxThroughput: uint64(pt.maxThroughput), + Allocation: float32(pt.allocation), + }) + } + } + peers = append(peers, p2) + } + C.ZT_Node_freeQueryResult(unsafe.Pointer(n.zn), unsafe.Pointer(pl)) + } + return peers +} + ////////////////////////////////////////////////////////////////////////////// func (n *Node) multicastSubscribe(nwid uint64, mg *MulticastGroup) { diff --git a/go/pkg/zerotier/path.go b/go/pkg/zerotier/path.go new file mode 100644 index 00000000..3d0e2e13 --- /dev/null +++ b/go/pkg/zerotier/path.go @@ -0,0 +1,34 @@ +/* + * 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" + +// Path is a path to another peer on the network +type Path struct { + IP net.IP + Port int + LastSend int64 + LastReceive int64 + TrustedPathID uint64 + Latency float32 + PacketDelayVariance float32 + ThroughputDisturbCoeff float32 + PacketErrorRatio float32 + PacketLossRatio float32 + Stability float32 + Throughput uint64 + MaxThroughput uint64 + Allocation float32 +} diff --git a/go/pkg/zerotier/peer.go b/go/pkg/zerotier/peer.go new file mode 100644 index 00000000..9687942a --- /dev/null +++ b/go/pkg/zerotier/peer.go @@ -0,0 +1,23 @@ +/* + * 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 + +// Peer is another ZeroTier node +type Peer struct { + Address Address + Version [3]int + Latency int + Role int + Paths []Path +} diff --git a/include/ZeroTierCore.h b/include/ZeroTierCore.h index b9242477..1f0a4f45 100644 --- a/include/ZeroTierCore.h +++ b/include/ZeroTierCore.h @@ -1171,12 +1171,12 @@ typedef struct /** * Time of last send in milliseconds or 0 for never */ - uint64_t lastSend; + int64_t lastSend; /** * Time of last receive in milliseconds or 0 for never */ - uint64_t lastReceive; + int64_t lastReceive; /** * Is this a trusted path? If so this will be its nonzero ID.