mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2025-02-20 17:52:46 +00:00
.
This commit is contained in:
parent
5175636d36
commit
7061f13b24
45
go/cmd/zerotier/cli/common.go
Normal file
45
go/cmd/zerotier/cli/common.go
Normal file
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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"
|
||||
"net/http"
|
||||
"os"
|
||||
"zerotier/pkg/zerotier"
|
||||
)
|
||||
|
||||
func apiGet(basePath, authToken, urlPath string, result interface{}) {
|
||||
statusCode, err := zerotier.APIGet(basePath, zerotier.APISocketName, authToken, urlPath, result)
|
||||
if err != nil {
|
||||
fmt.Printf("FATAL: API response code %d: %s\n", statusCode, err.Error())
|
||||
os.Exit(1)
|
||||
return
|
||||
}
|
||||
if statusCode != http.StatusOK {
|
||||
if statusCode == http.StatusUnauthorized {
|
||||
fmt.Printf("FATAL: API response code %d: unauthorized (authorization token incorrect)\n", statusCode)
|
||||
}
|
||||
fmt.Printf("FATAL: API response code %d\n", statusCode)
|
||||
os.Exit(1)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func enabledDisabled(f bool) string {
|
||||
if f {
|
||||
return "ENABLED"
|
||||
}
|
||||
return "DISABLED"
|
||||
}
|
@ -13,6 +13,51 @@
|
||||
|
||||
package cli
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"zerotier/pkg/zerotier"
|
||||
)
|
||||
|
||||
// Peers CLI command
|
||||
func Peers(basePath, authToken string, args []string) {
|
||||
func Peers(basePath, authToken string, args []string, jsonOutput bool) {
|
||||
var peers []zerotier.Peer
|
||||
apiGet(basePath, authToken, "/peer", &peers)
|
||||
|
||||
if jsonOutput {
|
||||
j, _ := json.MarshalIndent(&peers, "", " ")
|
||||
fmt.Println(string(j))
|
||||
} else {
|
||||
fmt.Printf("<ztaddr> <ver> <role> <lat> <link> <lastTX> <lastRX> <path(s)>\n")
|
||||
for _, peer := range peers {
|
||||
role := "LEAF"
|
||||
link := "RELAY"
|
||||
lastTX, lastRX := int64(0), int64(0)
|
||||
address := ""
|
||||
if len(peer.Paths) > 0 {
|
||||
link = "DIRECT"
|
||||
lastTX, lastRX = peer.Clock-peer.Paths[0].LastSend, peer.Clock-peer.Paths[0].LastReceive
|
||||
if lastTX < 0 {
|
||||
lastTX = 0
|
||||
}
|
||||
if lastRX < 0 {
|
||||
lastRX = 0
|
||||
}
|
||||
address = fmt.Sprintf("%s/%d", peer.Paths[0].IP.String(), peer.Paths[0].Port)
|
||||
}
|
||||
fmt.Printf("%.10x %-7s %-6s %-5d %-6s %-8d %-8d %s\n",
|
||||
uint64(peer.Address),
|
||||
fmt.Sprintf("%d.%d.%d", peer.Version[0], peer.Version[1], peer.Version[2]),
|
||||
role,
|
||||
peer.Latency,
|
||||
link,
|
||||
lastTX,
|
||||
lastRX,
|
||||
address,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
os.Exit(0)
|
||||
}
|
||||
|
@ -16,7 +16,6 @@ package cli
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"zerotier/pkg/zerotier"
|
||||
)
|
||||
@ -24,25 +23,50 @@ import (
|
||||
// Status shows service status info
|
||||
func Status(basePath, authToken string, args []string, jsonOutput bool) {
|
||||
var status zerotier.APIStatus
|
||||
statusCode, err := zerotier.APIGet(basePath, zerotier.APISocketName, authToken, "/status", &status)
|
||||
if err != nil {
|
||||
fmt.Printf("FATAL: API response code %d: %s\n", statusCode, err.Error())
|
||||
os.Exit(1)
|
||||
return
|
||||
}
|
||||
if statusCode != http.StatusOK {
|
||||
if statusCode == http.StatusUnauthorized {
|
||||
fmt.Printf("FATAL: API response code %d: unauthorized (authorization token incorrect)\n", statusCode)
|
||||
}
|
||||
fmt.Printf("FATAL: API response code %d\n", statusCode)
|
||||
os.Exit(1)
|
||||
return
|
||||
}
|
||||
apiGet(basePath, authToken, "/status", &status)
|
||||
|
||||
if jsonOutput {
|
||||
j, _ := json.MarshalIndent(&status, "", " ")
|
||||
fmt.Println(string(j))
|
||||
} else {
|
||||
online := "ONLINE"
|
||||
if !status.Online {
|
||||
online = "OFFLINE"
|
||||
}
|
||||
fmt.Printf("%.10x: %s %s\n", uint64(status.Address), online, status.Version)
|
||||
fmt.Printf("\tports: %d %d %d\n", status.Config.Settings.PrimaryPort, status.Config.Settings.SecondaryPort, status.Config.Settings.TertiaryPort)
|
||||
fmt.Printf("\tport search: %s\n", enabledDisabled(status.Config.Settings.PortSearch))
|
||||
fmt.Printf("\tport mapping (uPnP/NAT-PMP): %s\n", enabledDisabled(status.Config.Settings.PortMapping))
|
||||
fmt.Printf("\tmultipath mode: %d\n", status.Config.Settings.MuiltipathMode)
|
||||
fmt.Printf("\tblacklisted interface prefixes: ")
|
||||
for i, bl := range status.Config.Settings.InterfacePrefixBlacklist {
|
||||
if i > 0 {
|
||||
fmt.Print(',')
|
||||
}
|
||||
fmt.Print(bl)
|
||||
}
|
||||
fmt.Printf("\n\texplicit external addresses: ")
|
||||
for i, ea := range status.Config.Settings.ExplicitAddresses {
|
||||
if i > 0 {
|
||||
fmt.Print(',')
|
||||
}
|
||||
fmt.Print(ea.String())
|
||||
}
|
||||
fmt.Printf("\n\tsystem interface addresses: ")
|
||||
for i, a := range status.InterfaceAddresses {
|
||||
if i > 0 {
|
||||
fmt.Print(',')
|
||||
}
|
||||
fmt.Print(a.String())
|
||||
}
|
||||
fmt.Printf("\n\tmapped external addresses: ")
|
||||
for i, a := range status.MappedExternalAddresses {
|
||||
if i > 0 {
|
||||
fmt.Print(',')
|
||||
}
|
||||
fmt.Print(a.String())
|
||||
}
|
||||
fmt.Printf("\n\tidentity: %s\n", status.Identity.String())
|
||||
}
|
||||
|
||||
os.Exit(0)
|
||||
|
@ -111,7 +111,7 @@ func main() {
|
||||
cli.Status(basePath, authToken, cmdArgs, *jflag)
|
||||
case "peers", "listpeers":
|
||||
authTokenRequired(authToken)
|
||||
cli.Peers(basePath, authToken, cmdArgs)
|
||||
cli.Peers(basePath, authToken, cmdArgs, *jflag)
|
||||
case "roots":
|
||||
authTokenRequired(authToken)
|
||||
cli.Roots(basePath, authToken, cmdArgs)
|
||||
|
@ -96,6 +96,7 @@ type APIStatus struct {
|
||||
|
||||
// APINetwork is the object returned by API network inquiries
|
||||
type APINetwork struct {
|
||||
ID NetworkID
|
||||
Config *NetworkConfig
|
||||
Settings *NetworkLocalSettings
|
||||
MulticastSubscriptions []*MulticastGroup
|
||||
@ -104,6 +105,20 @@ type APINetwork struct {
|
||||
TapDeviceEnabled bool
|
||||
}
|
||||
|
||||
func apiNetworkFromNetwork(n *Network) *APINetwork {
|
||||
var nn APINetwork
|
||||
nn.ID = n.ID()
|
||||
c := n.Config()
|
||||
nn.Config = &c
|
||||
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()
|
||||
return &nn
|
||||
}
|
||||
|
||||
func apiSetStandardHeaders(out http.ResponseWriter) {
|
||||
now := time.Now().UTC()
|
||||
h := out.Header()
|
||||
@ -215,6 +230,7 @@ func createAPIServer(basePath string, node *Node) (*http.Server, error) {
|
||||
if req.Method == http.MethodPost || req.Method == http.MethodPut {
|
||||
var c LocalConfig
|
||||
if apiReadObj(out, req, &c) == nil {
|
||||
node.SetLocalConfig(&c)
|
||||
apiSendObj(out, req, http.StatusOK, node.LocalConfig())
|
||||
}
|
||||
} else if req.Method == http.MethodGet || req.Method == http.MethodHead {
|
||||
@ -279,12 +295,40 @@ func createAPIServer(basePath string, node *Node) (*http.Server, error) {
|
||||
if req.Method == http.MethodPost || req.Method == http.MethodPut {
|
||||
if queriedID == 0 {
|
||||
apiSendObj(out, req, http.StatusBadRequest, nil)
|
||||
} else {
|
||||
var nw APINetwork
|
||||
if apiReadObj(out, req, &nw) == nil {
|
||||
n := node.GetNetwork(nw.ID)
|
||||
if n == nil {
|
||||
n, err := node.Join(nw.ID, nw.Settings, nil)
|
||||
if err != nil {
|
||||
apiSendObj(out, req, http.StatusBadRequest, nil)
|
||||
} else {
|
||||
apiSendObj(out, req, http.StatusOK, apiNetworkFromNetwork(n))
|
||||
}
|
||||
} else {
|
||||
if nw.Settings != nil {
|
||||
n.SetLocalSettings(nw.Settings)
|
||||
}
|
||||
apiSendObj(out, req, http.StatusOK, apiNetworkFromNetwork(n))
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if req.Method == http.MethodGet || req.Method == http.MethodHead {
|
||||
networks := node.Networks()
|
||||
if queriedID == 0 { // no queried ID lists all networks
|
||||
networks := node.Networks()
|
||||
apiSendObj(out, req, http.StatusOK, networks)
|
||||
nws := make([]*APINetwork, 0, len(networks))
|
||||
for _, nw := range networks {
|
||||
nws = append(nws, apiNetworkFromNetwork(nw))
|
||||
}
|
||||
apiSendObj(out, req, http.StatusOK, nws)
|
||||
} else {
|
||||
for _, nw := range networks {
|
||||
if nw.ID() == queriedID {
|
||||
apiSendObj(out, req, http.StatusOK, apiNetworkFromNetwork(nw))
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
out.Header().Set("Allow", "GET, HEAD, PUT, POST")
|
||||
|
@ -194,6 +194,13 @@ func (n *Network) Config() NetworkConfig {
|
||||
// SetLocalSettings modifies this network's local settings
|
||||
func (n *Network) SetLocalSettings(ls *NetworkLocalSettings) { n.updateConfig(nil, ls) }
|
||||
|
||||
// LocalSettings gets this network's current local settings
|
||||
func (n *Network) LocalSettings() NetworkLocalSettings {
|
||||
n.configLock.RLock()
|
||||
defer n.configLock.RUnlock()
|
||||
return n.settings
|
||||
}
|
||||
|
||||
// MulticastSubscribe subscribes to a multicast group
|
||||
func (n *Network) MulticastSubscribe(mg *MulticastGroup) {
|
||||
n.node.log.Printf("%.16x joined multicast group %s", mg.String())
|
||||
|
@ -464,10 +464,13 @@ func (n *Node) SetLocalConfig(lc *LocalConfig) (restartRequired bool, err error)
|
||||
|
||||
// Join joins a network
|
||||
// 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 NetworkID, settings *NetworkLocalSettings, tap Tap) (*Network, error) {
|
||||
n.networksLock.RLock()
|
||||
if nw, have := n.networks[NetworkID(nwid)]; have {
|
||||
if nw, have := n.networks[nwid]; have {
|
||||
n.log.Printf("join network %.16x ignored: already a member", nwid)
|
||||
if settings != nil {
|
||||
nw.SetLocalSettings(settings)
|
||||
}
|
||||
return nw, nil
|
||||
}
|
||||
n.networksLock.RUnlock()
|
||||
@ -488,8 +491,11 @@ func (n *Node) Join(nwid uint64, tap Tap) (*Network, error) {
|
||||
return nil, err
|
||||
}
|
||||
n.networksLock.Lock()
|
||||
n.networks[NetworkID(nwid)] = nw
|
||||
n.networks[nwid] = nw
|
||||
n.networksLock.Unlock()
|
||||
if settings != nil {
|
||||
nw.SetLocalSettings(settings)
|
||||
}
|
||||
|
||||
return nw, nil
|
||||
}
|
||||
@ -504,6 +510,14 @@ func (n *Node) Leave(nwid uint64) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetNetwork looks up a network by ID or returns nil if not joined
|
||||
func (n *Node) GetNetwork(nwid NetworkID) *Network {
|
||||
n.networksLock.RLock()
|
||||
nw := n.networks[nwid]
|
||||
n.networksLock.RUnlock()
|
||||
return nw
|
||||
}
|
||||
|
||||
// Networks returns a list of networks that this node has joined
|
||||
func (n *Node) Networks() []*Network {
|
||||
var nws []*Network
|
||||
@ -613,30 +627,35 @@ func (n *Node) Peers() []*Peer {
|
||||
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),
|
||||
})
|
||||
if pt.alive != 0 {
|
||||
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),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
sort.Slice(p2.Paths, func(a, b int) bool { return p2.Paths[a].LastReceive < p2.Paths[b].LastReceive })
|
||||
p2.Clock = TimeMs()
|
||||
peers = append(peers, p2)
|
||||
}
|
||||
C.ZT_Node_freeQueryResult(unsafe.Pointer(n.zn), unsafe.Pointer(pl))
|
||||
}
|
||||
sort.Slice(peers, func(a, b int) bool { return peers[a].Address < peers[b].Address })
|
||||
return peers
|
||||
}
|
||||
|
||||
|
@ -20,4 +20,5 @@ type Peer struct {
|
||||
Latency int
|
||||
Role int
|
||||
Paths []Path
|
||||
Clock int64
|
||||
}
|
||||
|
@ -1234,9 +1234,9 @@ typedef struct
|
||||
char *ifname;
|
||||
|
||||
/**
|
||||
* Is path expired?
|
||||
* Is path alive?
|
||||
*/
|
||||
int expired;
|
||||
int alive;
|
||||
|
||||
/**
|
||||
* Is path preferred?
|
||||
|
@ -537,6 +537,7 @@ ZT_PeerList *Node::peers() const
|
||||
p->latency = -1;
|
||||
p->role = RR->topology->isRoot((*pi)->identity()) ? ZT_PEER_ROLE_PLANET : ZT_PEER_ROLE_LEAF;
|
||||
|
||||
const int64_t now = _now;
|
||||
std::vector< SharedPtr<Path> > paths((*pi)->paths(_now));
|
||||
SharedPtr<Path> bestp((*pi)->getAppropriatePath(_now,false));
|
||||
p->hadAggregateLink |= (*pi)->hasAggregateLink();
|
||||
@ -546,7 +547,7 @@ ZT_PeerList *Node::peers() const
|
||||
p->paths[p->pathCount].lastSend = (*path)->lastOut();
|
||||
p->paths[p->pathCount].lastReceive = (*path)->lastIn();
|
||||
p->paths[p->pathCount].trustedPathId = RR->topology->getOutboundPathTrust((*path)->address());
|
||||
p->paths[p->pathCount].expired = 0;
|
||||
p->paths[p->pathCount].alive = (*path)->alive(now) ? 1 : 0;
|
||||
p->paths[p->pathCount].preferred = ((*path) == bestp) ? 1 : 0;
|
||||
p->paths[p->pathCount].latency = (float)(*path)->latency();
|
||||
p->paths[p->pathCount].packetDelayVariance = (*path)->packetDelayVariance();
|
||||
|
Loading…
x
Reference in New Issue
Block a user