From c3e0f262d1c501d46a5feac6aa911f1e59b4b3fb Mon Sep 17 00:00:00 2001 From: Adam Ierymenko <adam.ierymenko@zerotier.com> Date: Wed, 2 Oct 2019 09:34:44 -0700 Subject: [PATCH] Regularize JSON stuff --- go/cmd/zerotier/cli/network.go | 4 +- go/cmd/zerotier/cli/networks.go | 2 +- go/pkg/zerotier/address.go | 2 +- go/pkg/zerotier/api.go | 135 ++++++++++++++++++------------ go/pkg/zerotier/errors.go | 7 ++ go/pkg/zerotier/localconfig.go | 28 +++---- go/pkg/zerotier/locator.go | 14 ++-- go/pkg/zerotier/multicastgroup.go | 4 +- go/pkg/zerotier/nativetap.go | 26 ++++-- go/pkg/zerotier/path.go | 28 +++---- go/pkg/zerotier/peer.go | 12 +-- go/pkg/zerotier/root.go | 4 +- go/pkg/zerotier/route.go | 12 +-- 13 files changed, 164 insertions(+), 114 deletions(-) diff --git a/go/cmd/zerotier/cli/network.go b/go/cmd/zerotier/cli/network.go index bce85cc5f..54bb7683b 100644 --- a/go/cmd/zerotier/cli/network.go +++ b/go/cmd/zerotier/cli/network.go @@ -48,7 +48,7 @@ func Network(basePath, authToken string, args []string, jsonOutput bool) { fmt.Printf("%s: %s\n", nwids, network.Config.Name) fmt.Printf("\tstatus:\t%s\n", networkStatusStr(network.Config.Status)) enabled := "no" - if network.TapDeviceEnabled { + if network.PortEnabled { enabled = "yes" } bridge := "no" @@ -59,7 +59,7 @@ func Network(basePath, authToken string, args []string, jsonOutput bool) { if network.Config.BroadcastEnabled { broadcast = "on" } - fmt.Printf("\tport:\t%s dev %s type %s mtu %d enabled %s bridge %s broadcast %s\n", network.Config.MAC.String(), network.TapDeviceName, network.TapDeviceType, network.Config.MTU, enabled, bridge, broadcast) + fmt.Printf("\tport:\t%s dev %s type %s mtu %d enabled %s bridge %s broadcast %s\n", network.Config.MAC.String(), network.PortName, network.PortType, network.Config.MTU, enabled, bridge, broadcast) fmt.Printf("\tmanaged addresses:\t") for i, a := range network.Config.AssignedAddresses { if i > 0 { diff --git a/go/cmd/zerotier/cli/networks.go b/go/cmd/zerotier/cli/networks.go index 8abf36486..46d39e728 100644 --- a/go/cmd/zerotier/cli/networks.go +++ b/go/cmd/zerotier/cli/networks.go @@ -34,7 +34,7 @@ func Networks(basePath, authToken string, args []string, jsonOutput bool) { if nw.Config.Type == zerotier.NetworkTypePublic { t = "PUBLIC" } - fmt.Printf("%.16x %-24s %-17s %-16s %-7s %-16s ", uint64(nw.ID), nw.Config.Name, nw.Config.MAC.String(), networkStatusStr(nw.Config.Status), t, nw.TapDeviceName) + fmt.Printf("%.16x %-24s %-17s %-16s %-7s %-16s ", uint64(nw.ID), nw.Config.Name, nw.Config.MAC.String(), networkStatusStr(nw.Config.Status), t, nw.PortName) for i, ip := range nw.Config.AssignedAddresses { if i > 0 { fmt.Print(',') diff --git a/go/pkg/zerotier/address.go b/go/pkg/zerotier/address.go index 5131bf290..95d3e133b 100644 --- a/go/pkg/zerotier/address.go +++ b/go/pkg/zerotier/address.go @@ -49,7 +49,7 @@ func (a Address) String() string { // MarshalJSON marshals this Address as a string func (a Address) MarshalJSON() ([]byte, error) { - return []byte("\"" + a.String() + "\""), nil + return []byte(fmt.Sprintf("\"%.10x\"", uint64(a))), nil } // UnmarshalJSON unmarshals this Address from a string diff --git a/go/pkg/zerotier/api.go b/go/pkg/zerotier/api.go index 887e16e02..5d72f8ddc 100644 --- a/go/pkg/zerotier/api.go +++ b/go/pkg/zerotier/api.go @@ -22,6 +22,7 @@ import ( "net" "net/http" "path" + "runtime" "strings" "time" @@ -31,6 +32,8 @@ import ( // APISocketName is the default socket name for accessing the API const APISocketName = "apisocket" +var startTime = TimeMs() + // APIGet makes a query to the API via a Unix domain or windows pipe socket func APIGet(basePath, socketName, authToken, queryPath string, obj interface{}) (int, error) { client, err := createNamedSocketHTTPClient(basePath, socketName) @@ -106,31 +109,38 @@ func APIDelete(basePath, socketName, authToken, queryPath string, result interfa // APIStatus is the object returned by API status inquiries type APIStatus struct { - Address Address - Clock int64 - Config LocalConfig - Online bool - PeerCount int - PathCount int - Identity *Identity - InterfaceAddresses []net.IP `json:",omitempty"` - MappedExternalAddresses []*InetAddress `json:",omitempty"` - Version string - VersionMajor int - VersionMinor int - VersionRevision int - VersionBuild int + Address Address `json:"address"` + Clock int64 `json:"clock"` + StartupTime int64 `json:"startupTime"` + Config LocalConfig `json:"config"` + Online bool `json:"online"` + PeerCount int `json:"peerCount"` + PathCount int `json:"pathCount"` + Identity *Identity `json:"identity"` + InterfaceAddresses []net.IP `json:"interfaceAddresses,omitempty"` + MappedExternalAddresses []*InetAddress `json:"mappedExternalAddresses,omitempty"` + Version string `json:"version"` + VersionMajor int `json:"versionMajor"` + VersionMinor int `json:"versionMinor"` + VersionRevision int `json:"versionRevision"` + VersionBuild int `json:"versionBuild"` + OS string `json:"os"` + Architecture string `json:"architecture"` + Concurrency int `json:"cpus"` + Runtime string `json:"runtimeVersion"` } // APINetwork is the object returned by API network inquiries type APINetwork struct { - ID NetworkID - Config NetworkConfig - Settings *NetworkLocalSettings `json:",omitempty"` - MulticastSubscriptions []*MulticastGroup `json:",omitempty"` - TapDeviceType string - TapDeviceName string - TapDeviceEnabled bool + ID NetworkID `json:"id"` + Config NetworkConfig `json:"config"` + Settings *NetworkLocalSettings `json:"settings,omitempty"` + MulticastSubscriptions []*MulticastGroup `json:"multicastSubscriptions,omitempty"` + PortType string `json:"portType"` + PortName string `json:"portName"` + PortEnabled bool `json:"portEnabled"` + PortErrorCode int `json:"portErrorCode"` + PortError string `json:"portError"` } func apiNetworkFromNetwork(n *Network) *APINetwork { @@ -140,19 +150,21 @@ func apiNetworkFromNetwork(n *Network) *APINetwork { ls := n.LocalSettings() nn.Settings = &ls nn.MulticastSubscriptions = n.MulticastSubscriptions() - nn.TapDeviceType = n.Tap().Type() - nn.TapDeviceName = n.Tap().DeviceName() - nn.TapDeviceEnabled = n.Tap().Enabled() + nn.PortType = n.Tap().Type() + nn.PortName = n.Tap().DeviceName() + nn.PortEnabled = n.Tap().Enabled() + ec, errStr := n.Tap().Error() + nn.PortErrorCode = ec + nn.PortError = errStr return &nn } func apiSetStandardHeaders(out http.ResponseWriter) { - now := time.Now().UTC() h := out.Header() h.Set("Cache-Control", "no-cache, no-store, must-revalidate") h.Set("Expires", "0") h.Set("Pragma", "no-cache") - h.Set("Date", now.Format(time.RFC1123)) + h.Set("Date", time.Now().UTC().Format(time.RFC1123)) } func apiSendObj(out http.ResponseWriter, req *http.Request, httpStatusCode int, obj interface{}) error { @@ -178,7 +190,7 @@ func apiSendObj(out http.ResponseWriter, req *http.Request, httpStatusCode int, func apiReadObj(out http.ResponseWriter, req *http.Request, dest interface{}) (err error) { err = json.NewDecoder(req.Body).Decode(&dest) if err != nil { - _ = apiSendObj(out, req, http.StatusBadRequest, nil) + _ = apiSendObj(out, req, http.StatusBadRequest, &APIErr{"invalid JSON: " + err.Error()}) } return } @@ -192,7 +204,7 @@ func apiCheckAuth(out http.ResponseWriter, req *http.Request, token string) bool if len(ah) > 0 && strings.TrimSpace(ah) == token { return true } - _ = apiSendObj(out, req, http.StatusUnauthorized, nil) + _ = apiSendObj(out, req, http.StatusUnauthorized, &APIErr{"authorization token not found or incorrect (checked X-ZT1-Auth and Authorization headers)"}) return false } @@ -223,6 +235,8 @@ func createAPIServer(basePath string, node *Node) (*http.Server, *http.Server, e smux := http.NewServeMux() + //////////////////////////////////////////////////////////////////////////// + smux.HandleFunc("/status", func(out http.ResponseWriter, req *http.Request) { defer func() { e := recover() @@ -234,8 +248,8 @@ func createAPIServer(basePath string, node *Node) (*http.Server, *http.Server, e if !apiCheckAuth(out, req, authToken) { return } - apiSetStandardHeaders(out) + if req.Method == http.MethodGet || req.Method == http.MethodHead { pathCount := 0 peers := node.Peers() @@ -245,6 +259,7 @@ func createAPIServer(basePath string, node *Node) (*http.Server, *http.Server, e _ = apiSendObj(out, req, http.StatusOK, &APIStatus{ Address: node.Address(), Clock: TimeMs(), + StartupTime: startTime, Config: node.LocalConfig(), Online: node.Online(), PeerCount: len(peers), @@ -257,34 +272,41 @@ func createAPIServer(basePath string, node *Node) (*http.Server, *http.Server, e VersionMinor: CoreVersionMinor, VersionRevision: CoreVersionRevision, VersionBuild: CoreVersionBuild, + OS: runtime.GOOS, + Architecture: runtime.GOARCH, + Concurrency: runtime.NumCPU(), + Runtime: runtime.Version(), }) } else { out.Header().Set("Allow", "GET, HEAD") - _ = apiSendObj(out, req, http.StatusMethodNotAllowed, nil) + _ = apiSendObj(out, req, http.StatusMethodNotAllowed, &APIErr{"/status is read-only"}) } }) + //////////////////////////////////////////////////////////////////////////// + smux.HandleFunc("/config", func(out http.ResponseWriter, req *http.Request) { defer func() { e := recover() if e != nil { - _ = apiSendObj(out, req, http.StatusInternalServerError, nil) + _ = apiSendObj(out, req, http.StatusInternalServerError, &APIErr{"caught unexpected error in request handler"}) } }() if !apiCheckAuth(out, req, authToken) { return } - apiSetStandardHeaders(out) + if req.Method == http.MethodPost || req.Method == http.MethodPut { var c LocalConfig if apiReadObj(out, req, &c) == nil { _, err := node.SetLocalConfig(&c) if err != nil { - _ = apiSendObj(out, req, http.StatusBadRequest, nil) + _ = apiSendObj(out, req, http.StatusBadRequest, &APIErr{"error applying local config: " + err.Error()}) } else { - _ = apiSendObj(out, req, http.StatusOK, node.LocalConfig()) + lc := node.LocalConfig() + _ = apiSendObj(out, req, http.StatusOK, &lc) } } } else if req.Method == http.MethodGet || req.Method == http.MethodHead { @@ -295,18 +317,19 @@ func createAPIServer(basePath string, node *Node) (*http.Server, *http.Server, e } }) + //////////////////////////////////////////////////////////////////////////// + smux.HandleFunc("/peer/", func(out http.ResponseWriter, req *http.Request) { defer func() { e := recover() if e != nil { - _ = apiSendObj(out, req, http.StatusInternalServerError, nil) + _ = apiSendObj(out, req, http.StatusInternalServerError, &APIErr{"caught unexpected error in request handler"}) } }() if !apiCheckAuth(out, req, authToken) { return } - apiSetStandardHeaders(out) var queriedID Address @@ -314,7 +337,7 @@ func createAPIServer(basePath string, node *Node) (*http.Server, *http.Server, e var err error queriedID, err = NewAddressFromString(req.URL.Path[6:]) if err != nil { - _ = apiSendObj(out, req, http.StatusNotFound, nil) + _ = apiSendObj(out, req, http.StatusNotFound, &APIErr{"peer not found"}) return } } @@ -328,28 +351,29 @@ func createAPIServer(basePath string, node *Node) (*http.Server, *http.Server, e return } } - _ = apiSendObj(out, req, http.StatusNotFound, nil) + _ = apiSendObj(out, req, http.StatusNotFound, &APIErr{"peer not found"}) } else { _ = apiSendObj(out, req, http.StatusOK, peers) } } else { out.Header().Set("Allow", "GET, HEAD") - _ = apiSendObj(out, req, http.StatusMethodNotAllowed, nil) + _ = apiSendObj(out, req, http.StatusMethodNotAllowed, &APIErr{"peers are read only"}) } }) + //////////////////////////////////////////////////////////////////////////// + smux.HandleFunc("/network/", func(out http.ResponseWriter, req *http.Request) { defer func() { e := recover() if e != nil { - _ = apiSendObj(out, req, http.StatusInternalServerError, nil) + _ = apiSendObj(out, req, http.StatusInternalServerError, &APIErr{"caught unexpected error in request handler"}) } }() if !apiCheckAuth(out, req, authToken) { return } - apiSetStandardHeaders(out) var queriedID NetworkID @@ -374,7 +398,7 @@ func createAPIServer(basePath string, node *Node) (*http.Server, *http.Server, e return } } - _ = apiSendObj(out, req, http.StatusNotFound, nil) + _ = apiSendObj(out, req, http.StatusNotFound, &APIErr{"network not found"}) } } else if req.Method == http.MethodPost || req.Method == http.MethodPut { if queriedID == 0 { @@ -386,7 +410,7 @@ func createAPIServer(basePath string, node *Node) (*http.Server, *http.Server, e if n == nil { n, err := node.Join(nw.ID, nw.Settings, nil) if err != nil { - _ = apiSendObj(out, req, http.StatusBadRequest, nil) + _ = apiSendObj(out, req, http.StatusBadRequest, &APIErr{"only individual networks can be added or modified with POST/PUT"}) } else { _ = apiSendObj(out, req, http.StatusOK, apiNetworkFromNetwork(n)) } @@ -413,26 +437,27 @@ func createAPIServer(basePath string, node *Node) (*http.Server, *http.Server, e return } } - _ = apiSendObj(out, req, http.StatusNotFound, nil) + _ = apiSendObj(out, req, http.StatusNotFound, &APIErr{"network not found"}) } } else { out.Header().Set("Allow", "GET, HEAD, PUT, POST, DELETE") - _ = apiSendObj(out, req, http.StatusMethodNotAllowed, nil) + _ = apiSendObj(out, req, http.StatusMethodNotAllowed, &APIErr{"unsupported method " + req.Method}) } }) + //////////////////////////////////////////////////////////////////////////// + smux.HandleFunc("/root/", func(out http.ResponseWriter, req *http.Request) { defer func() { e := recover() if e != nil { - _ = apiSendObj(out, req, http.StatusInternalServerError, nil) + _ = apiSendObj(out, req, http.StatusInternalServerError, &APIErr{"caught unexpected error in request handler"}) } }() if !apiCheckAuth(out, req, authToken) { return } - apiSetStandardHeaders(out) var queriedName string @@ -453,15 +478,19 @@ func createAPIServer(basePath string, node *Node) (*http.Server, *http.Server, e } _ = apiSendObj(out, req, http.StatusNotFound, nil) } else if req.Method == http.MethodPost || req.Method == http.MethodPut { + if len(queriedName) == 0 { + _ = apiSendObj(out, req, http.StatusBadRequest, &APIErr{"only individual roots can be added or modified with POST/PUT"}) + return + } var r Root if apiReadObj(out, req, &r) == nil { if r.Name != queriedName { - _ = apiSendObj(out, req, http.StatusBadRequest, nil) + _ = apiSendObj(out, req, http.StatusBadRequest, &APIErr{"root name does not match name in path"}) return } err := node.SetRoot(r.Name, r.Locator) if err != nil { - _ = apiSendObj(out, req, http.StatusBadRequest, nil) + _ = apiSendObj(out, req, http.StatusBadRequest, &APIErr{"set/update root failed: " + err.Error()}) } else { roots := node.Roots() for _, r := range roots { @@ -470,7 +499,7 @@ func createAPIServer(basePath string, node *Node) (*http.Server, *http.Server, e return } } - _ = apiSendObj(out, req, http.StatusNotFound, nil) + _ = apiSendObj(out, req, http.StatusNotFound, &APIErr{"set/update root failed: root set but not subsequently found in list"}) } } } else if req.Method == http.MethodGet || req.Method == http.MethodHead { @@ -481,13 +510,15 @@ func createAPIServer(basePath string, node *Node) (*http.Server, *http.Server, e return } } - _ = apiSendObj(out, req, http.StatusNotFound, nil) + _ = apiSendObj(out, req, http.StatusNotFound, &APIErr{"root not found"}) } else { out.Header().Set("Allow", "GET, HEAD, PUT, POST, DELETE") - _ = apiSendObj(out, req, http.StatusMethodNotAllowed, nil) + _ = apiSendObj(out, req, http.StatusMethodNotAllowed, &APIErr{"unsupported method: " + req.Method}) } }) + //////////////////////////////////////////////////////////////////////////// + listener, err := createNamedSocketListener(basePath, APISocketName) if err != nil { return nil, nil, err diff --git a/go/pkg/zerotier/errors.go b/go/pkg/zerotier/errors.go index 5dedc87fd..f9716f9f1 100644 --- a/go/pkg/zerotier/errors.go +++ b/go/pkg/zerotier/errors.go @@ -32,3 +32,10 @@ const ( ErrInvalidSignature Err = "invalid signature" ErrSecretKeyRequired Err = "secret key required" ) + +// APIErr is returned by the JSON API when a call fails +type APIErr struct { + Reason string +} + +func (e *APIErr) Error() string { return e.Reason } diff --git a/go/pkg/zerotier/localconfig.go b/go/pkg/zerotier/localconfig.go index 632c58d12..c5a210aae 100644 --- a/go/pkg/zerotier/localconfig.go +++ b/go/pkg/zerotier/localconfig.go @@ -39,49 +39,49 @@ type LocalConfigVirtualAddressConfiguration struct { // LocalConfigSettings contains node settings type LocalConfigSettings struct { // PrimaryPort is the main UDP port and must be set (defaults to 9993) - PrimaryPort int + PrimaryPort int `json:"primaryPort"` // SecondaryPort is the secondary UDP port, set to 0 to disbale (picked at random by default) - SecondaryPort int + SecondaryPort int `json:"secondaryPort"` // TertiaryPort is a third UDP port, set to 0 to disable (picked at random by default) - TertiaryPort int + TertiaryPort int `json:"tertiaryPort"` // PortSearch causes ZeroTier to try other ports automatically if it can't bind to configured ports - PortSearch bool + PortSearch bool `json:"portSearch"` // PortMapping enables uPnP and NAT-PMP support - PortMapping bool + PortMapping bool `json:"portMapping"` // LogSizeMax is the maximum size of the log in kilobytes or 0 for no limit and -1 to disable logging - LogSizeMax int + LogSizeMax int `json:"logSizeMax"` // MultipathMode sets the multipath link aggregation mode - MuiltipathMode int + MuiltipathMode int `json:"multipathMode"` // IP/port to bind for TCP access to control API (disabled if null) - APITCPBindAddress *InetAddress `json:",omitempty"` + APITCPBindAddress *InetAddress `json:"apiTCPBindAddress,omitempty"` // InterfacePrefixBlacklist are prefixes of physical network interface names that won't be used by ZeroTier (e.g. "lo" or "utun") - InterfacePrefixBlacklist []string `json:",omitempty"` + InterfacePrefixBlacklist []string `json:"interfacePrefixBlacklist,omitempty"` // ExplicitAddresses are explicit IP/port addresses to advertise to other nodes, such as externally mapped ports on a router - ExplicitAddresses []*InetAddress `json:",omitempty"` + ExplicitAddresses []*InetAddress `json:"explicitAddresses,omitempty"` } // LocalConfig is the local.conf file and stores local settings for the node. type LocalConfig struct { // Physical path configurations by CIDR IP/bits - Physical map[string]*LocalConfigPhysicalPathConfiguration `json:",omitempty"` + Physical map[string]*LocalConfigPhysicalPathConfiguration `json:"physical,omitempty"` // Virtual node specific configurations by 10-digit hex ZeroTier address - Virtual map[Address]*LocalConfigVirtualAddressConfiguration `json:",omitempty"` + Virtual map[Address]*LocalConfigVirtualAddressConfiguration `json:"virtual,omitempty"` // Network local configurations by 16-digit hex ZeroTier network ID - Network map[NetworkID]*NetworkLocalSettings `json:",omitempty"` + Network map[NetworkID]*NetworkLocalSettings `json:"network,omitempty"` // LocalConfigSettings contains other local settings for this node - Settings LocalConfigSettings `json:",omitempty"` + Settings LocalConfigSettings `json:"settings,omitempty"` } // Read this local config from a file, initializing to defaults if the file does not exist diff --git a/go/pkg/zerotier/locator.go b/go/pkg/zerotier/locator.go index 8c3beaaa2..ae9298362 100644 --- a/go/pkg/zerotier/locator.go +++ b/go/pkg/zerotier/locator.go @@ -24,8 +24,8 @@ import ( // LocatorDNSSigningKey is the public (as a secure DNS name) and private keys for entering locators into DNS type LocatorDNSSigningKey struct { - SecureDNSName string - PrivateKey []byte + SecureDNSName string `json:"secureDNSName"` + PrivateKey []byte `json:"privateKey"` } // NewLocatorDNSSigningKey creates a new signing key and secure DNS name for storing locators in DNS @@ -47,16 +47,16 @@ func NewLocatorDNSSigningKey() (*LocatorDNSSigningKey, error) { // and the others are always reconstructed from it. type Locator struct { // Identity is the full identity of the node being located - Identity *Identity + Identity *Identity `json:"identity"` // Physical is a list of static physical network addresses for this node - Physical []*InetAddress + Physical []*InetAddress `json:"physical,omitempty"` // Virtual is a list of ZeroTier nodes that can relay to this node - Virtual []*Identity + Virtual []*Identity `json:"virtual,omitempty"` // Bytes is the raw serialized Locator - Bytes []byte + Bytes []byte `json:"bytes,omitempty"` } // NewLocator creates a new locator with the given identity and addresses and the current time as timestamp. @@ -172,7 +172,7 @@ func (l *Locator) MakeTXTRecords(key *LocatorDNSSigningKey) ([]string, error) { } type locatorForUnmarshal struct { - Bytes []byte + Bytes []byte `json:"bytes,omitempty"` } // UnmarshalJSON unmarshals this Locator from a byte array in JSON. diff --git a/go/pkg/zerotier/multicastgroup.go b/go/pkg/zerotier/multicastgroup.go index 27b30ccb8..d85713ff7 100644 --- a/go/pkg/zerotier/multicastgroup.go +++ b/go/pkg/zerotier/multicastgroup.go @@ -17,8 +17,8 @@ import "fmt" // MulticastGroup represents a normal Ethernet multicast or broadcast address plus 32 additional ZeroTier-specific bits type MulticastGroup struct { - MAC MAC - ADI uint32 + MAC MAC `json:"mac"` + ADI uint32 `json:"adi"` } // String returns MAC#ADI diff --git a/go/pkg/zerotier/nativetap.go b/go/pkg/zerotier/nativetap.go index dae784116..560f9c2c7 100644 --- a/go/pkg/zerotier/nativetap.go +++ b/go/pkg/zerotier/nativetap.go @@ -11,6 +11,8 @@ */ /****/ +// This wraps EthernetTap from osdep/ + package zerotier //#cgo CFLAGS: -O3 @@ -159,17 +161,21 @@ func (t *nativeTap) AddMulticastGroupChangeHandler(handler func(bool, *Multicast func (t *nativeTap) AddRoute(r *Route) error { rc := 0 if r != nil { + var via []byte + if r.Via != nil { + via = *r.Via + } if len(r.Target.IP) == 4 { mask, _ := r.Target.Mask.Size() - if len(r.Via) == 4 { - rc = int(C.ZT_GoTap_addRoute(t.tap, AFInet, unsafe.Pointer(&r.Target.IP[0]), C.int(mask), AFInet, unsafe.Pointer(&r.Via[0]), C.uint(r.Metric))) + if len(via) == 4 { + rc = int(C.ZT_GoTap_addRoute(t.tap, AFInet, unsafe.Pointer(&r.Target.IP[0]), C.int(mask), AFInet, unsafe.Pointer(&via[0]), C.uint(r.Metric))) } else { rc = int(C.ZT_GoTap_addRoute(t.tap, AFInet, unsafe.Pointer(&r.Target.IP[0]), C.int(mask), 0, nil, C.uint(r.Metric))) } } else if len(r.Target.IP) == 16 { mask, _ := r.Target.Mask.Size() - if len(r.Via) == 4 { - rc = int(C.ZT_GoTap_addRoute(t.tap, AFInet6, unsafe.Pointer(&r.Target.IP[0]), C.int(mask), AFInet6, unsafe.Pointer(&r.Via[0]), C.uint(r.Metric))) + if len(via) == 16 { + rc = int(C.ZT_GoTap_addRoute(t.tap, AFInet6, unsafe.Pointer(&r.Target.IP[0]), C.int(mask), AFInet6, unsafe.Pointer(&via[0]), C.uint(r.Metric))) } else { rc = int(C.ZT_GoTap_addRoute(t.tap, AFInet6, unsafe.Pointer(&r.Target.IP[0]), C.int(mask), 0, nil, C.uint(r.Metric))) } @@ -185,17 +191,21 @@ func (t *nativeTap) AddRoute(r *Route) error { func (t *nativeTap) RemoveRoute(r *Route) error { rc := 0 if r != nil { + var via []byte + if r.Via != nil { + via = *r.Via + } if len(r.Target.IP) == 4 { mask, _ := r.Target.Mask.Size() - if len(r.Via) == 4 { - rc = int(C.ZT_GoTap_removeRoute(t.tap, AFInet, unsafe.Pointer(&r.Target.IP[0]), C.int(mask), AFInet, unsafe.Pointer(&r.Via[0]), C.uint(r.Metric))) + if len(via) == 4 { + rc = int(C.ZT_GoTap_removeRoute(t.tap, AFInet, unsafe.Pointer(&r.Target.IP[0]), C.int(mask), AFInet, unsafe.Pointer(&(via[0])), C.uint(r.Metric))) } else { rc = int(C.ZT_GoTap_removeRoute(t.tap, AFInet, unsafe.Pointer(&r.Target.IP[0]), C.int(mask), 0, nil, C.uint(r.Metric))) } } else if len(r.Target.IP) == 16 { mask, _ := r.Target.Mask.Size() - if len(r.Via) == 4 { - rc = int(C.ZT_GoTap_removeRoute(t.tap, AFInet6, unsafe.Pointer(&r.Target.IP[0]), C.int(mask), AFInet6, unsafe.Pointer(&r.Via[0]), C.uint(r.Metric))) + if len(via) == 16 { + rc = int(C.ZT_GoTap_removeRoute(t.tap, AFInet6, unsafe.Pointer(&r.Target.IP[0]), C.int(mask), AFInet6, unsafe.Pointer(&via[0]), C.uint(r.Metric))) } else { rc = int(C.ZT_GoTap_removeRoute(t.tap, AFInet6, unsafe.Pointer(&r.Target.IP[0]), C.int(mask), 0, nil, C.uint(r.Metric))) } diff --git a/go/pkg/zerotier/path.go b/go/pkg/zerotier/path.go index 3d0e2e138..d4d2672f6 100644 --- a/go/pkg/zerotier/path.go +++ b/go/pkg/zerotier/path.go @@ -17,18 +17,18 @@ 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 + IP net.IP `json:"ip"` + Port int `json:"port"` + LastSend int64 `json:"lastSend"` + LastReceive int64 `json:"lastReceive"` + TrustedPathID uint64 `json:"trustedPathID"` + Latency float32 `json:"latency"` + PacketDelayVariance float32 `json:"packetDelayVariance"` + ThroughputDisturbCoeff float32 `json:"throughputDisturbCoeff"` + PacketErrorRatio float32 `json:"packetErrorRatio"` + PacketLossRatio float32 `json:"packetLossRatio"` + Stability float32 `json:"stability"` + Throughput uint64 `json:"throughput"` + MaxThroughput uint64 `json:"maxThroughput"` + Allocation float32 `json:"allocation"` } diff --git a/go/pkg/zerotier/peer.go b/go/pkg/zerotier/peer.go index 792f76875..053252916 100644 --- a/go/pkg/zerotier/peer.go +++ b/go/pkg/zerotier/peer.go @@ -15,10 +15,10 @@ package zerotier // Peer is another ZeroTier node type Peer struct { - Address Address - Version [3]int - Latency int - Role int - Paths []Path - Clock int64 + Address Address `json:"address"` + Version [3]int `json:"version"` + Latency int `json:"latency"` + Role int `json:"role"` + Paths []Path `json:"paths,omitempty"` + Clock int64 `json:"clock"` } diff --git a/go/pkg/zerotier/root.go b/go/pkg/zerotier/root.go index 98afc79ca..73affb178 100644 --- a/go/pkg/zerotier/root.go +++ b/go/pkg/zerotier/root.go @@ -15,6 +15,6 @@ package zerotier // Root describes a root server used to find and establish communication with other nodes. type Root struct { - Name string - Locator *Locator + Name string `json:"name"` + Locator *Locator `json:"locator,omitempty"` } diff --git a/go/pkg/zerotier/route.go b/go/pkg/zerotier/route.go index 44805bc1f..8ff515ff1 100644 --- a/go/pkg/zerotier/route.go +++ b/go/pkg/zerotier/route.go @@ -21,16 +21,16 @@ import ( // Route represents a route in a host's routing table type Route struct { // Target for this route - Target net.IPNet + Target net.IPNet `json:"target"` // Via is how to reach this target (null/empty if the target IP range is local to this virtual LAN) - Via *net.IP + Via *net.IP `json:"via,omitempty"` // Route flags (currently unused, always 0) - Flags uint16 + Flags uint16 `json:"flags"` // Metric is an interface metric that can affect route priority (behavior can be OS-specific) - Metric uint16 + Metric uint16 `json:"metric"` } // String returns a string representation of this route @@ -46,7 +46,9 @@ func (r *Route) key() (k [6]uint64) { copy(((*[16]byte)(unsafe.Pointer(&k[0])))[:], r.Target.IP) ones, bits := r.Target.Mask.Size() k[2] = (uint64(ones) << 32) | uint64(bits) - copy(((*[16]byte)(unsafe.Pointer(&k[3])))[:], r.Via) + if r.Via != nil { + copy(((*[16]byte)(unsafe.Pointer(&k[3])))[:], *r.Via) + } k[5] = (uint64(r.Flags) << 32) | uint64(r.Metric) return }