This commit is contained in:
Adam Ierymenko 2019-09-25 12:36:49 -07:00
parent e5bd230fb0
commit 8a9669f130
No known key found for this signature in database
GPG key ID: C8877CF2D7A5D7F3
6 changed files with 307 additions and 79 deletions

View file

@ -0,0 +1,76 @@
/*
* 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 cli
import (
"fmt"
"zerotier/pkg/zerotier"
)
var copyrightText = fmt.Sprintf(`ZeroTier Network Virtualization Service Version %d.%d.%d
(c)2019 ZeroTier, Inc.
Licensed under the ZeroTier BSL (see LICENSE.txt)`, zerotier.CoreVersionMajor, zerotier.CoreVersionMinor, zerotier.CoreVersionRevision)
// Help dumps help to stdout
func Help() {
fmt.Println(copyrightText + `
Usage: zerotier [-options] <command> [-options] [command args]
Global Options
-j Output raw JSON where applicable
-p <path> Use alternate base path
-t <authtoken.secret path> Use secret auth token from this file
Commands:
help Show this help
version Print version
service Start in system service mode
status Show ZeroTier service status and config
peers Show VL1 peers
roots Show VL1 root servers
addroot <type> [options] Add a VL1 root
static <identity> <ip/port> [...] Add a root with a set identity and IPs
dynamic <name> [default locator] Add a dynamic root fetched by name
removeroot <type> [options] Remove a VL1 root
static <identity> Remove a root with a set identity
dynamic <name> Remove a dynamic root fetched by name
networks Show joined VL2 virtual networks
join <network ID> Join a virtual network
leave <network ID> Leave a virtual network
show <network ID> Show verbose network info
set <network ID> <option> <value> Set a network local config option
manageips <boolean> Is IP management allowed?
manageroutes <boolean> Is route management allowed?
globalips <boolean> Can IPs in global IP space be managed?
globalroutes <boolean> Can global IP space routes be set?
defaultroute <boolean> Can default route be overridden?
set <local config option> <value> Set a local configuration option
phy <IP/bits> blacklist <boolean> Set or clear blacklist for CIDR
phy <IP/bits> trust <path ID/0> Set or clear trusted path ID for CIDR
virt <address> try <IP/port> [...] Set explicit IPs for reaching a peer
port <port> Set primary local port for VL1 P2P
secondaryport <port/0> Set or disable secondary VL1 P2P port
tertiaryport <port/0> Set or disable tertiary VL1 P2P port
portsearch <boolean> Set or disable port search on startup
portmapping <boolean> Set or disable use of uPnP and NAT-PMP
explicitaddresses <IP/port> [...] Set explicit external IPs to advertise
Most commands require a secret token to permit control of a running ZeroTier
service. The CLI will automatically try to read this token from the
authtoken.secret file in the service's working directory and then from a
file called .zerotierauth in the user's home directory. The -t option can be
used to explicitly specify a location.
`)
}

View file

@ -13,17 +13,33 @@
package cli package cli
/* import (
func nodeStart() { "fmt"
"os"
"os/signal"
"syscall"
"zerotier/pkg/zerotier"
)
// Service is "zerotier service ..."
func Service(basePath, authToken string, args []string) {
if len(args) > 0 {
Help()
os.Exit(1)
}
node, err := zerotier.NewNode(basePath)
if err != nil {
fmt.Println("FATAL: error initializing node: " + err.Error())
os.Exit(1)
}
osSignalChannel := make(chan os.Signal, 2) osSignalChannel := make(chan os.Signal, 2)
signal.Notify(osSignalChannel, syscall.SIGTERM, syscall.SIGQUIT, syscall.SIGINT, syscall.SIGBUS) signal.Notify(osSignalChannel, syscall.SIGTERM, syscall.SIGQUIT, syscall.SIGINT, syscall.SIGBUS)
signal.Ignore(syscall.SIGUSR1, syscall.SIGUSR2) signal.Ignore(syscall.SIGUSR1, syscall.SIGUSR2)
go func() { go func() {
<-osSignalChannel <-osSignalChannel
node.Close()
os.Exit(0)
}() }()
} }
*/
// Service is "zerotier service ..."
func Service(basePath, authToken string, args []string) {
}

View file

@ -25,62 +25,6 @@ import (
"zerotier/pkg/zerotier" "zerotier/pkg/zerotier"
) )
var copyrightText = fmt.Sprintf(`ZeroTier Network Virtualization Service Version %d.%d.%d
(c)2019 ZeroTier, Inc.
Licensed under the ZeroTier BSL (see LICENSE.txt)`, zerotier.CoreVersionMajor, zerotier.CoreVersionMinor, zerotier.CoreVersionRevision)
func printHelp() {
fmt.Println(copyrightText + `
Usage: zerotier [-options] <command> [-options] [command args]
Global Options
-j Output raw JSON where applicable
-p <path> Connect to service running at this path
-t <authtoken.secret path> Use secret auth token from this file
Commands:
help Show this help
version Print version
service [path] Start in system service mode
status Show ZeroTier service status and config
peers Show VL1 peers
roots Show VL1 root servers
addroot <type> [options] Add a VL1 root
static <identity> <ip/port> [...] Add a root with a set identity and IPs
dynamic <name> [default locator] Add a dynamic root fetched by name
removeroot <type> [options] Remove a VL1 root
static <identity> Remove a root with a set identity
dynamic <name> Remove a dynamic root fetched by name
networks Show joined VL2 virtual networks
join <network ID> Join a virtual network
leave <network ID> Leave a virtual network
show <network ID> Show verbose network info
set <network ID> <option> <value> Set a network local config option
manageips <boolean> Is IP management allowed?
manageroutes <boolean> Is route management allowed?
globalips <boolean> Can IPs in global IP space be managed?
globalroutes <boolean> Can global IP space routes be set?
defaultroute <boolean> Can default route be overridden?
set <local config option> <value> Set a local configuration option
phy <IP/bits> blacklist <boolean> Set or clear blacklist for CIDR
phy <IP/bits> trust <path ID/0> Set or clear trusted path ID for CIDR
virt <address> try <IP/port> [...] Set explicit IPs for reaching a peer
port <port> Set primary local port for VL1 P2P
secondaryport <port/0> Set or disable secondary VL1 P2P port
tertiaryport <port/0> Set or disable tertiary VL1 P2P port
portsearch <boolean> Set or disable port search on startup
portmapping <boolean> Set or disable use of uPnP and NAT-PMP
explicitaddresses <IP/port> [...] Set explicit external IPs to advertise
Most commands require a secret token to permit control of a running ZeroTier
service. The CLI will automatically try to read this token from the
authtoken.secret file in the service's working directory and then from a
file called .zerotierauth in the user's home directory. The -t option can be
used to explicitly specify a location.
`)
}
func readAuthToken(basePath string) string { func readAuthToken(basePath string) string {
data, _ := ioutil.ReadFile(path.Join(basePath, "authtoken.secret")) data, _ := ioutil.ReadFile(path.Join(basePath, "authtoken.secret"))
if len(data) > 0 { if len(data) > 0 {
@ -118,13 +62,13 @@ func main() {
tflag := globalOpts.String("t", "", "") tflag := globalOpts.String("t", "", "")
err := globalOpts.Parse(os.Args[1:]) err := globalOpts.Parse(os.Args[1:])
if err != nil { if err != nil {
printHelp() cli.Help()
os.Exit(1) os.Exit(1)
return return
} }
args := globalOpts.Args() args := globalOpts.Args()
if len(args) < 1 || *hflag { if len(args) < 1 || *hflag {
printHelp() cli.Help()
os.Exit(0) os.Exit(0)
return return
} }
@ -151,7 +95,7 @@ func main() {
switch args[0] { switch args[0] {
case "help": case "help":
printHelp() cli.Help()
os.Exit(0) os.Exit(0)
case "version": case "version":
fmt.Printf("%d.%d.%d\n", zerotier.CoreVersionMajor, zerotier.CoreVersionMinor, zerotier.CoreVersionRevision) fmt.Printf("%d.%d.%d\n", zerotier.CoreVersionMajor, zerotier.CoreVersionMinor, zerotier.CoreVersionRevision)
@ -180,6 +124,6 @@ func main() {
cli.Set(basePath, authToken, cmdArgs) cli.Set(basePath, authToken, cmdArgs)
} }
printHelp() cli.Help()
os.Exit(1) os.Exit(1)
} }

View file

@ -53,6 +53,9 @@ type LocalConfigSettings struct {
// PortMapping enables uPnP and NAT-PMP support // PortMapping enables uPnP and NAT-PMP support
PortMapping bool PortMapping bool
// LogSizeMax is the maximum size of the log in kilobytes or 0 for no limit and -1 to disable logging
LogSizeMax int
// MultipathMode sets the multipath link aggregation mode // MultipathMode sets the multipath link aggregation mode
MuiltipathMode int MuiltipathMode int
@ -89,6 +92,7 @@ func (lc *LocalConfig) Read(p string, saveDefaultsIfNotExist bool) error {
lc.Settings.TertiaryPort = 32768 + (rand.Int() % 16384) lc.Settings.TertiaryPort = 32768 + (rand.Int() % 16384)
lc.Settings.PortSearch = true lc.Settings.PortSearch = true
lc.Settings.PortMapping = true lc.Settings.PortMapping = true
lc.Settings.LogSizeMax = 128
lc.Settings.MuiltipathMode = 0 lc.Settings.MuiltipathMode = 0
switch runtime.GOOS { switch runtime.GOOS {
case "darwin": case "darwin":

View file

@ -28,12 +28,14 @@ import (
"errors" "errors"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"log"
rand "math/rand" rand "math/rand"
"net" "net"
"net/http" "net/http"
"os" "os"
"path" "path"
"sort" "sort"
"strings"
"sync" "sync"
"sync/atomic" "sync/atomic"
"time" "time"
@ -42,6 +44,8 @@ import (
acl "github.com/hectane/go-acl" acl "github.com/hectane/go-acl"
) )
var nullLogger = log.New(ioutil.Discard, "", 0)
// Network status states // Network status states
const ( const (
NetworkStatusRequestConfiguration int = C.ZT_NETWORK_STATUS_REQUESTING_CONFIGURATION NetworkStatusRequestConfiguration int = C.ZT_NETWORK_STATUS_REQUESTING_CONFIGURATION
@ -159,6 +163,8 @@ type Node struct {
localConfigLock sync.RWMutex localConfigLock sync.RWMutex
networksLock sync.RWMutex networksLock sync.RWMutex
interfaceAddressesLock sync.Mutex interfaceAddressesLock sync.Mutex
logW *sizeLimitWriter
log *log.Logger
gn *C.ZT_GoNode gn *C.ZT_GoNode
zn *C.ZT_Node zn *C.ZT_Node
id *Identity id *Identity
@ -170,23 +176,36 @@ type Node struct {
// NewNode creates and initializes a new instance of the ZeroTier node service // NewNode creates and initializes a new instance of the ZeroTier node service
func NewNode(basePath string) (*Node, error) { func NewNode(basePath string) (*Node, error) {
var err error
os.MkdirAll(basePath, 0755) os.MkdirAll(basePath, 0755)
if _, err := os.Stat(basePath); err != nil { if _, err := os.Stat(basePath); err != nil {
return nil, err return nil, err
} }
n := new(Node) n := new(Node)
n.networks = make(map[NetworkID]*Network) n.networks = make(map[NetworkID]*Network)
n.networksByMAC = make(map[MAC]*Network) n.networksByMAC = make(map[MAC]*Network)
n.interfaceAddresses = make(map[string]net.IP) n.interfaceAddresses = make(map[string]net.IP)
n.basePath = basePath n.basePath = basePath
n.localConfigPath = path.Join(basePath, "local.conf") n.localConfigPath = path.Join(basePath, "local.conf")
err := n.localConfig.Read(n.localConfigPath, true) err = n.localConfig.Read(n.localConfigPath, true)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if n.localConfig.Settings.LogSizeMax >= 0 {
n.logW, err = sizeLimitWriterOpen(path.Join(basePath, "service.log"))
if err != nil {
return nil, err
}
n.log = log.New(n.logW, "", log.LstdFlags)
} else {
n.log = nullLogger
}
if n.localConfig.Settings.PortSearch { if n.localConfig.Settings.PortSearch {
portsChanged := false portsChanged := false
@ -196,6 +215,7 @@ func NewNode(basePath string) (*Node, error) {
if checkPort(n.localConfig.Settings.PrimaryPort) { if checkPort(n.localConfig.Settings.PrimaryPort) {
break break
} }
n.log.Printf("primary port %d unavailable, trying next port (port search enabled)", n.localConfig.Settings.PrimaryPort)
n.localConfig.Settings.PrimaryPort++ n.localConfig.Settings.PrimaryPort++
n.localConfig.Settings.PrimaryPort &= 0xffff n.localConfig.Settings.PrimaryPort &= 0xffff
portsChanged = true portsChanged = true
@ -211,6 +231,7 @@ func NewNode(basePath string) (*Node, error) {
if checkPort(n.localConfig.Settings.SecondaryPort) { if checkPort(n.localConfig.Settings.SecondaryPort) {
break break
} }
n.log.Printf("secondary port %d unavailable, trying next port (port search enabled)", n.localConfig.Settings.SecondaryPort)
n.localConfig.Settings.SecondaryPort++ n.localConfig.Settings.SecondaryPort++
n.localConfig.Settings.SecondaryPort &= 0xffff n.localConfig.Settings.SecondaryPort &= 0xffff
portsChanged = true portsChanged = true
@ -227,6 +248,7 @@ func NewNode(basePath string) (*Node, error) {
if checkPort(n.localConfig.Settings.TertiaryPort) { if checkPort(n.localConfig.Settings.TertiaryPort) {
break break
} }
n.log.Printf("tertiary port %d unavailable, trying next port (port search enabled)", n.localConfig.Settings.TertiaryPort)
n.localConfig.Settings.TertiaryPort++ n.localConfig.Settings.TertiaryPort++
n.localConfig.Settings.TertiaryPort &= 0xffff n.localConfig.Settings.TertiaryPort &= 0xffff
portsChanged = true portsChanged = true
@ -247,20 +269,24 @@ func NewNode(basePath string) (*Node, error) {
n.gn = C.ZT_GoNode_new(cpath) n.gn = C.ZT_GoNode_new(cpath)
C.free(unsafe.Pointer(cpath)) C.free(unsafe.Pointer(cpath))
if n.gn == nil { if n.gn == nil {
n.log.Println("FATAL: node initialization failed")
return nil, ErrNodeInitFailed return nil, ErrNodeInitFailed
} }
n.zn = (*C.ZT_Node)(C.ZT_GoNode_getNode(n.gn)) n.zn = (*C.ZT_Node)(C.ZT_GoNode_getNode(n.gn))
var ns C.ZT_NodeStatus var ns C.ZT_NodeStatus
C.ZT_Node_status(unsafe.Pointer(n.zn), &ns) C.ZT_Node_status(unsafe.Pointer(n.zn), &ns)
n.id, err = NewIdentityFromString(C.GoString(ns.secretIdentity)) idstr := C.GoString(ns.secretIdentity)
n.id, err = NewIdentityFromString(idstr)
if err != nil { if err != nil {
n.log.Printf("FATAL: node's identity does not seem valid (%s)", idstr)
C.ZT_GoNode_delete(n.gn) C.ZT_GoNode_delete(n.gn)
return nil, err return nil, err
} }
n.apiServer, err = createAPIServer(basePath, n) n.apiServer, err = createAPIServer(basePath, n)
if err != nil { if err != nil {
n.log.Printf("FATAL: unable to start API server: %s", err.Error())
C.ZT_GoNode_delete(n.gn) C.ZT_GoNode_delete(n.gn)
return nil, err return nil, err
} }
@ -275,13 +301,13 @@ func NewNode(basePath string) (*Node, error) {
n.runLock.Lock() n.runLock.Lock()
go func() { go func() {
lastScannedInterfaces := int64(0) lastMaintenanceRun := int64(0)
for atomic.LoadUint32(&n.running) != 0 { for atomic.LoadUint32(&n.running) != 0 {
time.Sleep(1 * time.Second) time.Sleep(1 * time.Second)
now := TimeMs() now := TimeMs()
if (now - lastScannedInterfaces) >= 30000 { if (now - lastMaintenanceRun) >= 30000 {
lastScannedInterfaces = now lastMaintenanceRun = now
interfaceAddresses := make(map[string]net.IP) interfaceAddresses := make(map[string]net.IP)
ifs, _ := net.Interfaces() ifs, _ := net.Interfaces()
@ -305,17 +331,21 @@ func NewNode(basePath string) (*Node, error) {
} }
n.localConfigLock.RLock() n.localConfigLock.RLock()
n.interfaceAddressesLock.Lock() n.interfaceAddressesLock.Lock()
for astr, ipn := range interfaceAddresses { for astr, ipn := range interfaceAddresses {
if _, alreadyKnown := n.interfaceAddresses[astr]; !alreadyKnown { if _, alreadyKnown := n.interfaceAddresses[astr]; !alreadyKnown {
ipCstr := C.CString(ipn.String()) ipCstr := C.CString(ipn.String())
if n.localConfig.Settings.PrimaryPort > 0 && n.localConfig.Settings.PrimaryPort < 65536 { if n.localConfig.Settings.PrimaryPort > 0 && n.localConfig.Settings.PrimaryPort < 65536 {
n.log.Printf("UDP binding to port %d on interface %s", n.localConfig.Settings.PrimaryPort, astr)
C.ZT_GoNode_phyStartListen(n.gn, nil, ipCstr, C.int(n.localConfig.Settings.PrimaryPort)) C.ZT_GoNode_phyStartListen(n.gn, nil, ipCstr, C.int(n.localConfig.Settings.PrimaryPort))
} }
if n.localConfig.Settings.SecondaryPort > 0 && n.localConfig.Settings.SecondaryPort < 65536 { if n.localConfig.Settings.SecondaryPort > 0 && n.localConfig.Settings.SecondaryPort < 65536 {
n.log.Printf("UDP binding to port %d on interface %s", n.localConfig.Settings.SecondaryPort, astr)
C.ZT_GoNode_phyStartListen(n.gn, nil, ipCstr, C.int(n.localConfig.Settings.SecondaryPort)) C.ZT_GoNode_phyStartListen(n.gn, nil, ipCstr, C.int(n.localConfig.Settings.SecondaryPort))
} }
if n.localConfig.Settings.TertiaryPort > 0 && n.localConfig.Settings.TertiaryPort < 65536 { if n.localConfig.Settings.TertiaryPort > 0 && n.localConfig.Settings.TertiaryPort < 65536 {
n.log.Printf("UDP binding to port %d on interface %s", n.localConfig.Settings.TertiaryPort, astr)
C.ZT_GoNode_phyStartListen(n.gn, nil, ipCstr, C.int(n.localConfig.Settings.TertiaryPort)) C.ZT_GoNode_phyStartListen(n.gn, nil, ipCstr, C.int(n.localConfig.Settings.TertiaryPort))
} }
C.free(unsafe.Pointer(ipCstr)) C.free(unsafe.Pointer(ipCstr))
@ -325,12 +355,15 @@ func NewNode(basePath string) (*Node, error) {
if _, stillPresent := interfaceAddresses[astr]; !stillPresent { if _, stillPresent := interfaceAddresses[astr]; !stillPresent {
ipCstr := C.CString(ipn.String()) ipCstr := C.CString(ipn.String())
if n.localConfig.Settings.PrimaryPort > 0 && n.localConfig.Settings.PrimaryPort < 65536 { if n.localConfig.Settings.PrimaryPort > 0 && n.localConfig.Settings.PrimaryPort < 65536 {
n.log.Printf("UDP closing socket bound to port %d on interface %s", n.localConfig.Settings.PrimaryPort, astr)
C.ZT_GoNode_phyStopListen(n.gn, nil, ipCstr, C.int(n.localConfig.Settings.PrimaryPort)) C.ZT_GoNode_phyStopListen(n.gn, nil, ipCstr, C.int(n.localConfig.Settings.PrimaryPort))
} }
if n.localConfig.Settings.SecondaryPort > 0 && n.localConfig.Settings.SecondaryPort < 65536 { if n.localConfig.Settings.SecondaryPort > 0 && n.localConfig.Settings.SecondaryPort < 65536 {
n.log.Printf("UDP closing socket bound to port %d on interface %s", n.localConfig.Settings.SecondaryPort, astr)
C.ZT_GoNode_phyStopListen(n.gn, nil, ipCstr, C.int(n.localConfig.Settings.SecondaryPort)) C.ZT_GoNode_phyStopListen(n.gn, nil, ipCstr, C.int(n.localConfig.Settings.SecondaryPort))
} }
if n.localConfig.Settings.TertiaryPort > 0 && n.localConfig.Settings.TertiaryPort < 65536 { if n.localConfig.Settings.TertiaryPort > 0 && n.localConfig.Settings.TertiaryPort < 65536 {
n.log.Printf("UDP closing socket bound to port %d on interface %s", n.localConfig.Settings.TertiaryPort, astr)
C.ZT_GoNode_phyStopListen(n.gn, nil, ipCstr, C.int(n.localConfig.Settings.TertiaryPort)) C.ZT_GoNode_phyStopListen(n.gn, nil, ipCstr, C.int(n.localConfig.Settings.TertiaryPort))
} }
C.free(unsafe.Pointer(ipCstr)) C.free(unsafe.Pointer(ipCstr))
@ -338,6 +371,11 @@ func NewNode(basePath string) (*Node, error) {
} }
n.interfaceAddresses = interfaceAddresses n.interfaceAddresses = interfaceAddresses
n.interfaceAddressesLock.Unlock() n.interfaceAddressesLock.Unlock()
if n.localConfig.Settings.LogSizeMax > 0 && n.logW != nil {
n.logW.trim(n.localConfig.Settings.LogSizeMax*1024, 0.5, true)
}
n.localConfigLock.RUnlock() n.localConfigLock.RUnlock()
} }
} }
@ -388,25 +426,64 @@ func (n *Node) LocalConfig() LocalConfig {
return n.localConfig return n.localConfig
} }
// SetLocalConfig updates this node's local configuration
func (n *Node) SetLocalConfig(lc *LocalConfig) (restartRequired bool, err error) {
n.networksLock.RLock()
n.localConfigLock.Lock()
defer n.localConfigLock.Unlock()
defer n.networksLock.RUnlock()
for nid, nc := range lc.Network {
nw := n.networks[nid]
if nw != nil {
nw.SetLocalSettings(nc)
}
}
if n.localConfig.Settings.PrimaryPort != lc.Settings.PrimaryPort || n.localConfig.Settings.SecondaryPort != lc.Settings.SecondaryPort || n.localConfig.Settings.TertiaryPort != lc.Settings.TertiaryPort {
restartRequired = true
}
if lc.Settings.LogSizeMax < 0 {
n.log = nullLogger
n.logW.Close()
n.logW = nil
} else if n.logW != nil {
n.logW, err = sizeLimitWriterOpen(path.Join(n.basePath, "service.log"))
if err == nil {
n.log = log.New(n.logW, "", log.LstdFlags)
} else {
n.log = nullLogger
n.logW = nil
}
}
n.localConfig = *lc
return
}
// Join joins a network // Join joins a network
// If tap is nil, the default system tap for this OS/platform is used (if available). // If tap is nil, the default system tap for this OS/platform is used (if available).
func (n *Node) Join(nwid uint64, tap Tap) (*Network, error) { func (n *Node) Join(nwid uint64, tap Tap) (*Network, error) {
n.networksLock.RLock() n.networksLock.RLock()
if nw, have := n.networks[NetworkID(nwid)]; have { if nw, have := n.networks[NetworkID(nwid)]; have {
n.log.Printf("join network %.16x ignored: already a member", nwid)
return nw, nil return nw, nil
} }
n.networksLock.RUnlock() n.networksLock.RUnlock()
if tap != nil { if tap != nil {
return nil, errors.New("non-native taps not implemented yet") panic("non-native taps not yet implemented")
} }
ntap := C.ZT_GoNode_join(n.gn, C.uint64_t(nwid)) ntap := C.ZT_GoNode_join(n.gn, C.uint64_t(nwid))
if ntap == nil { if ntap == nil {
n.log.Printf("join network %.16x failed: tap device failed to initialize (check drivers / kernel modules)")
return nil, ErrTapInitFailed return nil, ErrTapInitFailed
} }
nw, err := newNetwork(n, NetworkID(nwid), &nativeTap{tap: unsafe.Pointer(ntap), enabled: 1}) nw, err := newNetwork(n, NetworkID(nwid), &nativeTap{tap: unsafe.Pointer(ntap), enabled: 1})
if err != nil { if err != nil {
n.log.Printf("join network %.16x failed: network failed to initialize: %s", nwid, err.Error())
C.ZT_GoNode_leave(n.gn, C.uint64_t(nwid)) C.ZT_GoNode_leave(n.gn, C.uint64_t(nwid))
return nil, err return nil, err
} }
@ -419,6 +496,7 @@ func (n *Node) Join(nwid uint64, tap Tap) (*Network, error) {
// Leave leaves a network // Leave leaves a network
func (n *Node) Leave(nwid uint64) error { func (n *Node) Leave(nwid uint64) error {
n.log.Printf("leaving network %.16x", nwid)
C.ZT_GoNode_leave(n.gn, C.uint64_t(nwid)) C.ZT_GoNode_leave(n.gn, C.uint64_t(nwid))
n.networksLock.Lock() n.networksLock.Lock()
delete(n.networks, NetworkID(nwid)) delete(n.networks, NetworkID(nwid))
@ -439,15 +517,17 @@ func (n *Node) Networks() []*Network {
// AddStaticRoot adds a statically defined root server to this node. // AddStaticRoot adds a statically defined root server to this node.
// If a static root with the given identity already exists this will update its IP and port information. // If a static root with the given identity already exists this will update its IP and port information.
func (n *Node) AddStaticRoot(id *Identity, addrs []net.Addr) { func (n *Node) AddStaticRoot(id *Identity, addrs []InetAddress) {
var saddrs []C.struct_sockaddr_storage var saddrs []C.struct_sockaddr_storage
var straddrs strings.Builder
for _, a := range addrs { for _, a := range addrs {
aa, _ := a.(*net.UDPAddr) ss := new(C.struct_sockaddr_storage)
if aa != nil { if makeSockaddrStorage(a.IP, a.Port, ss) {
ss := new(C.struct_sockaddr_storage) saddrs = append(saddrs, *ss)
if makeSockaddrStorage(aa.IP, aa.Port, ss) { if straddrs.Len() > 0 {
saddrs = append(saddrs, *ss) straddrs.WriteString(",")
} }
straddrs.WriteString(a.String())
} }
} }
if len(saddrs) > 0 { if len(saddrs) > 0 {

View file

@ -0,0 +1,108 @@
/*
* 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 (
"os"
"sync"
)
type sizeLimitWriter struct {
f *os.File
l sync.Mutex
}
func sizeLimitWriterOpen(p string) (*sizeLimitWriter, error) {
f, err := os.OpenFile(p, os.O_CREATE|os.O_RDWR, 0644)
if err != nil {
return nil, err
}
f.Seek(0, os.SEEK_END)
return &sizeLimitWriter{f: f}, nil
}
// Write implements io.Writer
func (w *sizeLimitWriter) Write(b []byte) (int, error) {
w.l.Lock()
defer w.l.Unlock()
return w.f.Write(b)
}
// Close closes the underlying file
func (w *sizeLimitWriter) Close() error {
w.l.Lock()
defer w.l.Unlock()
return w.f.Close()
}
func (w *sizeLimitWriter) trim(maxSize int, trimFactor float64, trimAtCR bool) error {
w.l.Lock()
defer w.l.Unlock()
flen, err := w.f.Seek(0, os.SEEK_END)
if err != nil {
return err
}
if flen > int64(maxSize) {
var buf [131072]byte
trimAt := int64(float64(flen) * trimFactor)
if trimAt >= flen { // sanity check
return nil
}
if trimAtCR {
lookForCR:
for {
nr, err := w.f.ReadAt(buf[0:1024], trimAt)
if err != nil {
return err
}
for i := 0; i < nr; i++ {
trimAt++
if buf[i] == byte('\n') {
break lookForCR
}
}
if trimAt >= flen {
return nil
}
}
}
copyTo := int64(0)
for trimAt < flen {
nr, _ := w.f.ReadAt(buf[:], trimAt)
if nr > 0 {
wr, _ := w.f.WriteAt(buf[0:nr], copyTo)
if wr > 0 {
copyTo += int64(wr)
} else {
break
}
} else {
break
}
}
err = w.f.Truncate(copyTo)
if err != nil {
return err
}
_, err = w.f.Seek(0, os.SEEK_END)
return err
}
return nil
}