Regularize JSON stuff

This commit is contained in:
Adam Ierymenko 2019-10-02 09:34:44 -07:00
parent b9911d0db7
commit c3e0f262d1
No known key found for this signature in database
GPG Key ID: C8877CF2D7A5D7F3
13 changed files with 164 additions and 114 deletions

View File

@ -48,7 +48,7 @@ func Network(basePath, authToken string, args []string, jsonOutput bool) {
fmt.Printf("%s: %s\n", nwids, network.Config.Name) fmt.Printf("%s: %s\n", nwids, network.Config.Name)
fmt.Printf("\tstatus:\t%s\n", networkStatusStr(network.Config.Status)) fmt.Printf("\tstatus:\t%s\n", networkStatusStr(network.Config.Status))
enabled := "no" enabled := "no"
if network.TapDeviceEnabled { if network.PortEnabled {
enabled = "yes" enabled = "yes"
} }
bridge := "no" bridge := "no"
@ -59,7 +59,7 @@ func Network(basePath, authToken string, args []string, jsonOutput bool) {
if network.Config.BroadcastEnabled { if network.Config.BroadcastEnabled {
broadcast = "on" 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") fmt.Printf("\tmanaged addresses:\t")
for i, a := range network.Config.AssignedAddresses { for i, a := range network.Config.AssignedAddresses {
if i > 0 { if i > 0 {

View File

@ -34,7 +34,7 @@ func Networks(basePath, authToken string, args []string, jsonOutput bool) {
if nw.Config.Type == zerotier.NetworkTypePublic { if nw.Config.Type == zerotier.NetworkTypePublic {
t = "PUBLIC" 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 { for i, ip := range nw.Config.AssignedAddresses {
if i > 0 { if i > 0 {
fmt.Print(',') fmt.Print(',')

View File

@ -49,7 +49,7 @@ func (a Address) String() string {
// MarshalJSON marshals this Address as a string // MarshalJSON marshals this Address as a string
func (a Address) MarshalJSON() ([]byte, error) { 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 // UnmarshalJSON unmarshals this Address from a string

View File

@ -22,6 +22,7 @@ import (
"net" "net"
"net/http" "net/http"
"path" "path"
"runtime"
"strings" "strings"
"time" "time"
@ -31,6 +32,8 @@ import (
// APISocketName is the default socket name for accessing the API // APISocketName is the default socket name for accessing the API
const APISocketName = "apisocket" const APISocketName = "apisocket"
var startTime = TimeMs()
// APIGet makes a query to the API via a Unix domain or windows pipe socket // 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) { func APIGet(basePath, socketName, authToken, queryPath string, obj interface{}) (int, error) {
client, err := createNamedSocketHTTPClient(basePath, socketName) 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 // APIStatus is the object returned by API status inquiries
type APIStatus struct { type APIStatus struct {
Address Address Address Address `json:"address"`
Clock int64 Clock int64 `json:"clock"`
Config LocalConfig StartupTime int64 `json:"startupTime"`
Online bool Config LocalConfig `json:"config"`
PeerCount int Online bool `json:"online"`
PathCount int PeerCount int `json:"peerCount"`
Identity *Identity PathCount int `json:"pathCount"`
InterfaceAddresses []net.IP `json:",omitempty"` Identity *Identity `json:"identity"`
MappedExternalAddresses []*InetAddress `json:",omitempty"` InterfaceAddresses []net.IP `json:"interfaceAddresses,omitempty"`
Version string MappedExternalAddresses []*InetAddress `json:"mappedExternalAddresses,omitempty"`
VersionMajor int Version string `json:"version"`
VersionMinor int VersionMajor int `json:"versionMajor"`
VersionRevision int VersionMinor int `json:"versionMinor"`
VersionBuild int 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 // APINetwork is the object returned by API network inquiries
type APINetwork struct { type APINetwork struct {
ID NetworkID ID NetworkID `json:"id"`
Config NetworkConfig Config NetworkConfig `json:"config"`
Settings *NetworkLocalSettings `json:",omitempty"` Settings *NetworkLocalSettings `json:"settings,omitempty"`
MulticastSubscriptions []*MulticastGroup `json:",omitempty"` MulticastSubscriptions []*MulticastGroup `json:"multicastSubscriptions,omitempty"`
TapDeviceType string PortType string `json:"portType"`
TapDeviceName string PortName string `json:"portName"`
TapDeviceEnabled bool PortEnabled bool `json:"portEnabled"`
PortErrorCode int `json:"portErrorCode"`
PortError string `json:"portError"`
} }
func apiNetworkFromNetwork(n *Network) *APINetwork { func apiNetworkFromNetwork(n *Network) *APINetwork {
@ -140,19 +150,21 @@ func apiNetworkFromNetwork(n *Network) *APINetwork {
ls := n.LocalSettings() ls := n.LocalSettings()
nn.Settings = &ls nn.Settings = &ls
nn.MulticastSubscriptions = n.MulticastSubscriptions() nn.MulticastSubscriptions = n.MulticastSubscriptions()
nn.TapDeviceType = n.Tap().Type() nn.PortType = n.Tap().Type()
nn.TapDeviceName = n.Tap().DeviceName() nn.PortName = n.Tap().DeviceName()
nn.TapDeviceEnabled = n.Tap().Enabled() nn.PortEnabled = n.Tap().Enabled()
ec, errStr := n.Tap().Error()
nn.PortErrorCode = ec
nn.PortError = errStr
return &nn return &nn
} }
func apiSetStandardHeaders(out http.ResponseWriter) { func apiSetStandardHeaders(out http.ResponseWriter) {
now := time.Now().UTC()
h := out.Header() h := out.Header()
h.Set("Cache-Control", "no-cache, no-store, must-revalidate") h.Set("Cache-Control", "no-cache, no-store, must-revalidate")
h.Set("Expires", "0") h.Set("Expires", "0")
h.Set("Pragma", "no-cache") 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 { 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) { func apiReadObj(out http.ResponseWriter, req *http.Request, dest interface{}) (err error) {
err = json.NewDecoder(req.Body).Decode(&dest) err = json.NewDecoder(req.Body).Decode(&dest)
if err != nil { if err != nil {
_ = apiSendObj(out, req, http.StatusBadRequest, nil) _ = apiSendObj(out, req, http.StatusBadRequest, &APIErr{"invalid JSON: " + err.Error()})
} }
return return
} }
@ -192,7 +204,7 @@ func apiCheckAuth(out http.ResponseWriter, req *http.Request, token string) bool
if len(ah) > 0 && strings.TrimSpace(ah) == token { if len(ah) > 0 && strings.TrimSpace(ah) == token {
return true 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 return false
} }
@ -223,6 +235,8 @@ func createAPIServer(basePath string, node *Node) (*http.Server, *http.Server, e
smux := http.NewServeMux() smux := http.NewServeMux()
////////////////////////////////////////////////////////////////////////////
smux.HandleFunc("/status", func(out http.ResponseWriter, req *http.Request) { smux.HandleFunc("/status", func(out http.ResponseWriter, req *http.Request) {
defer func() { defer func() {
e := recover() e := recover()
@ -234,8 +248,8 @@ func createAPIServer(basePath string, node *Node) (*http.Server, *http.Server, e
if !apiCheckAuth(out, req, authToken) { if !apiCheckAuth(out, req, authToken) {
return return
} }
apiSetStandardHeaders(out) apiSetStandardHeaders(out)
if req.Method == http.MethodGet || req.Method == http.MethodHead { if req.Method == http.MethodGet || req.Method == http.MethodHead {
pathCount := 0 pathCount := 0
peers := node.Peers() peers := node.Peers()
@ -245,6 +259,7 @@ func createAPIServer(basePath string, node *Node) (*http.Server, *http.Server, e
_ = apiSendObj(out, req, http.StatusOK, &APIStatus{ _ = apiSendObj(out, req, http.StatusOK, &APIStatus{
Address: node.Address(), Address: node.Address(),
Clock: TimeMs(), Clock: TimeMs(),
StartupTime: startTime,
Config: node.LocalConfig(), Config: node.LocalConfig(),
Online: node.Online(), Online: node.Online(),
PeerCount: len(peers), PeerCount: len(peers),
@ -257,34 +272,41 @@ func createAPIServer(basePath string, node *Node) (*http.Server, *http.Server, e
VersionMinor: CoreVersionMinor, VersionMinor: CoreVersionMinor,
VersionRevision: CoreVersionRevision, VersionRevision: CoreVersionRevision,
VersionBuild: CoreVersionBuild, VersionBuild: CoreVersionBuild,
OS: runtime.GOOS,
Architecture: runtime.GOARCH,
Concurrency: runtime.NumCPU(),
Runtime: runtime.Version(),
}) })
} else { } else {
out.Header().Set("Allow", "GET, HEAD") 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) { smux.HandleFunc("/config", func(out http.ResponseWriter, req *http.Request) {
defer func() { defer func() {
e := recover() e := recover()
if e != nil { 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) { if !apiCheckAuth(out, req, authToken) {
return return
} }
apiSetStandardHeaders(out) apiSetStandardHeaders(out)
if req.Method == http.MethodPost || req.Method == http.MethodPut { if req.Method == http.MethodPost || req.Method == http.MethodPut {
var c LocalConfig var c LocalConfig
if apiReadObj(out, req, &c) == nil { if apiReadObj(out, req, &c) == nil {
_, err := node.SetLocalConfig(&c) _, err := node.SetLocalConfig(&c)
if err != nil { if err != nil {
_ = apiSendObj(out, req, http.StatusBadRequest, nil) _ = apiSendObj(out, req, http.StatusBadRequest, &APIErr{"error applying local config: " + err.Error()})
} else { } 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 { } 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) { smux.HandleFunc("/peer/", func(out http.ResponseWriter, req *http.Request) {
defer func() { defer func() {
e := recover() e := recover()
if e != nil { 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) { if !apiCheckAuth(out, req, authToken) {
return return
} }
apiSetStandardHeaders(out) apiSetStandardHeaders(out)
var queriedID Address var queriedID Address
@ -314,7 +337,7 @@ func createAPIServer(basePath string, node *Node) (*http.Server, *http.Server, e
var err error var err error
queriedID, err = NewAddressFromString(req.URL.Path[6:]) queriedID, err = NewAddressFromString(req.URL.Path[6:])
if err != nil { if err != nil {
_ = apiSendObj(out, req, http.StatusNotFound, nil) _ = apiSendObj(out, req, http.StatusNotFound, &APIErr{"peer not found"})
return return
} }
} }
@ -328,28 +351,29 @@ func createAPIServer(basePath string, node *Node) (*http.Server, *http.Server, e
return return
} }
} }
_ = apiSendObj(out, req, http.StatusNotFound, nil) _ = apiSendObj(out, req, http.StatusNotFound, &APIErr{"peer not found"})
} else { } else {
_ = apiSendObj(out, req, http.StatusOK, peers) _ = apiSendObj(out, req, http.StatusOK, peers)
} }
} else { } else {
out.Header().Set("Allow", "GET, HEAD") 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) { smux.HandleFunc("/network/", func(out http.ResponseWriter, req *http.Request) {
defer func() { defer func() {
e := recover() e := recover()
if e != nil { 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) { if !apiCheckAuth(out, req, authToken) {
return return
} }
apiSetStandardHeaders(out) apiSetStandardHeaders(out)
var queriedID NetworkID var queriedID NetworkID
@ -374,7 +398,7 @@ func createAPIServer(basePath string, node *Node) (*http.Server, *http.Server, e
return 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 { } else if req.Method == http.MethodPost || req.Method == http.MethodPut {
if queriedID == 0 { if queriedID == 0 {
@ -386,7 +410,7 @@ func createAPIServer(basePath string, node *Node) (*http.Server, *http.Server, e
if n == nil { if n == nil {
n, err := node.Join(nw.ID, nw.Settings, nil) n, err := node.Join(nw.ID, nw.Settings, nil)
if err != 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 { } else {
_ = apiSendObj(out, req, http.StatusOK, apiNetworkFromNetwork(n)) _ = apiSendObj(out, req, http.StatusOK, apiNetworkFromNetwork(n))
} }
@ -413,26 +437,27 @@ func createAPIServer(basePath string, node *Node) (*http.Server, *http.Server, e
return return
} }
} }
_ = apiSendObj(out, req, http.StatusNotFound, nil) _ = apiSendObj(out, req, http.StatusNotFound, &APIErr{"network not found"})
} }
} else { } else {
out.Header().Set("Allow", "GET, HEAD, PUT, POST, DELETE") 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) { smux.HandleFunc("/root/", func(out http.ResponseWriter, req *http.Request) {
defer func() { defer func() {
e := recover() e := recover()
if e != nil { 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) { if !apiCheckAuth(out, req, authToken) {
return return
} }
apiSetStandardHeaders(out) apiSetStandardHeaders(out)
var queriedName string var queriedName string
@ -453,15 +478,19 @@ func createAPIServer(basePath string, node *Node) (*http.Server, *http.Server, e
} }
_ = apiSendObj(out, req, http.StatusNotFound, nil) _ = apiSendObj(out, req, http.StatusNotFound, nil)
} else if req.Method == http.MethodPost || req.Method == http.MethodPut { } 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 var r Root
if apiReadObj(out, req, &r) == nil { if apiReadObj(out, req, &r) == nil {
if r.Name != queriedName { 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 return
} }
err := node.SetRoot(r.Name, r.Locator) err := node.SetRoot(r.Name, r.Locator)
if err != nil { if err != nil {
_ = apiSendObj(out, req, http.StatusBadRequest, nil) _ = apiSendObj(out, req, http.StatusBadRequest, &APIErr{"set/update root failed: " + err.Error()})
} else { } else {
roots := node.Roots() roots := node.Roots()
for _, r := range roots { for _, r := range roots {
@ -470,7 +499,7 @@ func createAPIServer(basePath string, node *Node) (*http.Server, *http.Server, e
return 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 { } 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 return
} }
} }
_ = apiSendObj(out, req, http.StatusNotFound, nil) _ = apiSendObj(out, req, http.StatusNotFound, &APIErr{"root not found"})
} else { } else {
out.Header().Set("Allow", "GET, HEAD, PUT, POST, DELETE") 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) listener, err := createNamedSocketListener(basePath, APISocketName)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err

View File

@ -32,3 +32,10 @@ const (
ErrInvalidSignature Err = "invalid signature" ErrInvalidSignature Err = "invalid signature"
ErrSecretKeyRequired Err = "secret key required" 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 }

View File

@ -39,49 +39,49 @@ type LocalConfigVirtualAddressConfiguration struct {
// LocalConfigSettings contains node settings // LocalConfigSettings contains node settings
type LocalConfigSettings struct { type LocalConfigSettings struct {
// PrimaryPort is the main UDP port and must be set (defaults to 9993) // 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 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 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 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 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 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 // 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) // 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 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 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. // LocalConfig is the local.conf file and stores local settings for the node.
type LocalConfig struct { type LocalConfig struct {
// Physical path configurations by CIDR IP/bits // 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 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 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 // 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 // Read this local config from a file, initializing to defaults if the file does not exist

View File

@ -24,8 +24,8 @@ import (
// LocatorDNSSigningKey is the public (as a secure DNS name) and private keys for entering locators into DNS // LocatorDNSSigningKey is the public (as a secure DNS name) and private keys for entering locators into DNS
type LocatorDNSSigningKey struct { type LocatorDNSSigningKey struct {
SecureDNSName string SecureDNSName string `json:"secureDNSName"`
PrivateKey []byte PrivateKey []byte `json:"privateKey"`
} }
// NewLocatorDNSSigningKey creates a new signing key and secure DNS name for storing locators in DNS // 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. // and the others are always reconstructed from it.
type Locator struct { type Locator struct {
// Identity is the full identity of the node being located // 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 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 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 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. // 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 { type locatorForUnmarshal struct {
Bytes []byte Bytes []byte `json:"bytes,omitempty"`
} }
// UnmarshalJSON unmarshals this Locator from a byte array in JSON. // UnmarshalJSON unmarshals this Locator from a byte array in JSON.

View File

@ -17,8 +17,8 @@ import "fmt"
// MulticastGroup represents a normal Ethernet multicast or broadcast address plus 32 additional ZeroTier-specific bits // MulticastGroup represents a normal Ethernet multicast or broadcast address plus 32 additional ZeroTier-specific bits
type MulticastGroup struct { type MulticastGroup struct {
MAC MAC MAC MAC `json:"mac"`
ADI uint32 ADI uint32 `json:"adi"`
} }
// String returns MAC#ADI // String returns MAC#ADI

View File

@ -11,6 +11,8 @@
*/ */
/****/ /****/
// This wraps EthernetTap from osdep/
package zerotier package zerotier
//#cgo CFLAGS: -O3 //#cgo CFLAGS: -O3
@ -159,17 +161,21 @@ func (t *nativeTap) AddMulticastGroupChangeHandler(handler func(bool, *Multicast
func (t *nativeTap) AddRoute(r *Route) error { func (t *nativeTap) AddRoute(r *Route) error {
rc := 0 rc := 0
if r != nil { if r != nil {
var via []byte
if r.Via != nil {
via = *r.Via
}
if len(r.Target.IP) == 4 { if len(r.Target.IP) == 4 {
mask, _ := r.Target.Mask.Size() mask, _ := r.Target.Mask.Size()
if len(r.Via) == 4 { 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(&r.Via[0]), C.uint(r.Metric))) 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 { } 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))) 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 { } else if len(r.Target.IP) == 16 {
mask, _ := r.Target.Mask.Size() mask, _ := r.Target.Mask.Size()
if len(r.Via) == 4 { 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(&r.Via[0]), C.uint(r.Metric))) 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 { } 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))) 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 { func (t *nativeTap) RemoveRoute(r *Route) error {
rc := 0 rc := 0
if r != nil { if r != nil {
var via []byte
if r.Via != nil {
via = *r.Via
}
if len(r.Target.IP) == 4 { if len(r.Target.IP) == 4 {
mask, _ := r.Target.Mask.Size() mask, _ := r.Target.Mask.Size()
if len(r.Via) == 4 { 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(&r.Via[0]), C.uint(r.Metric))) 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 { } 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))) 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 { } else if len(r.Target.IP) == 16 {
mask, _ := r.Target.Mask.Size() mask, _ := r.Target.Mask.Size()
if len(r.Via) == 4 { 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(&r.Via[0]), C.uint(r.Metric))) 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 { } 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))) rc = int(C.ZT_GoTap_removeRoute(t.tap, AFInet6, unsafe.Pointer(&r.Target.IP[0]), C.int(mask), 0, nil, C.uint(r.Metric)))
} }

View File

@ -17,18 +17,18 @@ import "net"
// Path is a path to another peer on the network // Path is a path to another peer on the network
type Path struct { type Path struct {
IP net.IP IP net.IP `json:"ip"`
Port int Port int `json:"port"`
LastSend int64 LastSend int64 `json:"lastSend"`
LastReceive int64 LastReceive int64 `json:"lastReceive"`
TrustedPathID uint64 TrustedPathID uint64 `json:"trustedPathID"`
Latency float32 Latency float32 `json:"latency"`
PacketDelayVariance float32 PacketDelayVariance float32 `json:"packetDelayVariance"`
ThroughputDisturbCoeff float32 ThroughputDisturbCoeff float32 `json:"throughputDisturbCoeff"`
PacketErrorRatio float32 PacketErrorRatio float32 `json:"packetErrorRatio"`
PacketLossRatio float32 PacketLossRatio float32 `json:"packetLossRatio"`
Stability float32 Stability float32 `json:"stability"`
Throughput uint64 Throughput uint64 `json:"throughput"`
MaxThroughput uint64 MaxThroughput uint64 `json:"maxThroughput"`
Allocation float32 Allocation float32 `json:"allocation"`
} }

View File

@ -15,10 +15,10 @@ package zerotier
// Peer is another ZeroTier node // Peer is another ZeroTier node
type Peer struct { type Peer struct {
Address Address Address Address `json:"address"`
Version [3]int Version [3]int `json:"version"`
Latency int Latency int `json:"latency"`
Role int Role int `json:"role"`
Paths []Path Paths []Path `json:"paths,omitempty"`
Clock int64 Clock int64 `json:"clock"`
} }

View File

@ -15,6 +15,6 @@ package zerotier
// Root describes a root server used to find and establish communication with other nodes. // Root describes a root server used to find and establish communication with other nodes.
type Root struct { type Root struct {
Name string Name string `json:"name"`
Locator *Locator Locator *Locator `json:"locator,omitempty"`
} }

View File

@ -21,16 +21,16 @@ import (
// Route represents a route in a host's routing table // Route represents a route in a host's routing table
type Route struct { type Route struct {
// Target for this route // 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 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) // 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 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 // 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) copy(((*[16]byte)(unsafe.Pointer(&k[0])))[:], r.Target.IP)
ones, bits := r.Target.Mask.Size() ones, bits := r.Target.Mask.Size()
k[2] = (uint64(ones) << 32) | uint64(bits) 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) k[5] = (uint64(r.Flags) << 32) | uint64(r.Metric)
return return
} }