diff --git a/README.md b/README.md
index 9816f42..7a440c9 100644
--- a/README.md
+++ b/README.md
@@ -293,6 +293,9 @@ Once the client configs have been imported and Wireguard is started, you can vie
╰─────────────────────╯
```
+> [!TIP]
+> Add the `--network-info` flag to this command to get a list of each Server host's network interfaces and associated CIDR addresses.
+
## Add Server (Optional)
diff --git a/src/api/api.go b/src/api/api.go
index e1d85ef..7e0008a 100644
--- a/src/api/api.go
+++ b/src/api/api.go
@@ -26,6 +26,9 @@ type request struct {
Body []byte
}
+// Re-export the server api struct so files importing this package can access it
+type HostInterface serverapi.HostInterface
+
// MakeRequest attempts to send an API query to the Wiretap server.
func makeRequest(req request) ([]byte, error) {
client := &http.Client{Timeout: 3 * time.Second}
@@ -89,6 +92,24 @@ func ServerInfo(apiAddr netip.AddrPort) (peer.Config, peer.Config, error) {
return *configs.RelayConfig, *configs.E2EEConfig, nil
}
+func ServerInterfaces(apiAddr netip.AddrPort) ([]HostInterface, error) {
+ body, err := makeRequest(request{
+ URL: makeUrl(apiAddr, "serverinterfaces", []string{}),
+ Method: "GET",
+ })
+ if err != nil {
+ return nil, err
+ }
+
+ var interfaces []HostInterface
+ err = json.Unmarshal(body, &interfaces)
+ if err != nil {
+ return nil, err
+ }
+
+ return interfaces, nil
+}
+
func AllocateNode(apiAddr netip.AddrPort, peerType peer.PeerType) (serverapi.NetworkState, error) {
body, err := makeRequest(request{
URL: makeUrl(apiAddr, "allocate", []string{fmt.Sprintf("type=%d", peerType)}),
diff --git a/src/cmd/status.go b/src/cmd/status.go
index de0540e..7415ad3 100644
--- a/src/cmd/status.go
+++ b/src/cmd/status.go
@@ -14,6 +14,7 @@ import (
)
type statusCmdConfig struct {
+ networkInfo bool
configFileRelay string
configFileE2EE string
}
@@ -21,6 +22,7 @@ type statusCmdConfig struct {
// Defaults for status command.
// See root command for shared defaults.
var statusCmd = statusCmdConfig{
+ networkInfo: false,
configFileRelay: ConfigRelay,
configFileE2EE: ConfigE2EE,
}
@@ -39,6 +41,7 @@ func init() {
rootCmd.AddCommand(cmd)
+ cmd.Flags().BoolVarP(&statusCmd.networkInfo, "network-info", "n", statusCmd.networkInfo, "Display network info for each online server node")
cmd.Flags().StringVarP(&statusCmd.configFileRelay, "relay", "1", statusCmd.configFileRelay, "wireguard relay config input filename")
cmd.Flags().StringVarP(&statusCmd.configFileE2EE, "e2ee", "2", statusCmd.configFileE2EE, "wireguard E2EE config input filename")
@@ -46,22 +49,23 @@ func init() {
}
// Run attempts to parse config files into a network diagram.
-func (c statusCmdConfig) Run() {
+func (cc statusCmdConfig) Run() {
// Start building tree.
type Node struct {
peerConfig peer.PeerConfig
relayConfig peer.Config
e2eeConfig peer.Config
children []*Node
+ interfaces []api.HostInterface
error string
}
var err error
// Parse the relay and e2ee config files
- clientConfigRelay, err := peer.ParseConfig(c.configFileRelay)
+ clientConfigRelay, err := peer.ParseConfig(cc.configFileRelay)
check("failed to parse relay config file", err)
- clientConfigE2EE, err := peer.ParseConfig(c.configFileE2EE)
+ clientConfigE2EE, err := peer.ParseConfig(cc.configFileE2EE)
check("failed to parse e2ee config file", err)
client := Node{
@@ -81,15 +85,26 @@ func (c statusCmdConfig) Run() {
relayConfig, e2eeConfig, err := api.ServerInfo(netip.AddrPortFrom(ep.GetApiAddr(), uint16(ApiPort)))
if err != nil {
errorNodes = append(errorNodes, Node{
- peerConfig: ep,
- error: err.Error(),
+ peerConfig: ep,
+ error: err.Error(),
})
-
+
} else {
+ var interfaces []api.HostInterface
+ if cc.networkInfo {
+ interfaces, err = api.ServerInterfaces(netip.AddrPortFrom(ep.GetApiAddr(), uint16(ApiPort)))
+ if err != nil {
+ interfaces = append(interfaces, api.HostInterface{
+ Name: "ERROR: " + err.Error(),
+ })
+ }
+ }
+
nodes[relayConfig.GetPublicKey()] = Node{
peerConfig: ep,
relayConfig: relayConfig,
e2eeConfig: e2eeConfig,
+ interfaces: interfaces,
}
}
}
@@ -131,19 +146,43 @@ func (c statusCmdConfig) Run() {
ips := []string{}
var api string
for j, a := range c.peerConfig.GetAllowedIPs() {
- if j == len(c.peerConfig.GetAllowedIPs()) - 1 {
+ if j == len(c.peerConfig.GetAllowedIPs())-1 {
api = a.IP.String()
} else {
ips = append(ips, a.String())
}
}
- t.AddChild(tree.NodeString(fmt.Sprintf(`server
+
+ nodeString := fmt.Sprintf(
+`server
nickname: %v
relay: %v...
e2ee: %v...
api: %v
- routes: %v `, c.peerConfig.GetNickname(), c.relayConfig.GetPublicKey()[:8], c.e2eeConfig.GetPublicKey()[:8], api, strings.Join(ips, ","))))
+ routes: %v `,
+ c.peerConfig.GetNickname(),
+ c.relayConfig.GetPublicKey()[:8],
+ c.e2eeConfig.GetPublicKey()[:8],
+ api,
+ strings.Join(ips, ","),
+ )
+
+ if cc.networkInfo {
+ nodeString += `
+
+Network Interfaces:
+-------------------
+`
+ for _, ifx := range c.interfaces {
+ nodeString += fmt.Sprintf("%v:\n", ifx.Name)
+ for _, a := range ifx.Addrs {
+ nodeString += strings.Repeat(" ", 2) + a.String() + "\n"
+ }
+ }
+ }
+
+ t.AddChild(tree.NodeString(nodeString))
child, err := t.Child(0)
check("could not build tree", err)
treeTraversal(node.children[i], child)
@@ -156,31 +195,39 @@ func (c statusCmdConfig) Run() {
fmt.Println()
fmt.Fprintln(color.Output, WhiteBold(t))
fmt.Println()
-
+
if len(errorNodes) > 0 {
// Display known peers that we had issues connecting to
fmt.Fprintln(color.Output, WhiteBold("Peers with Errors:"))
fmt.Println()
-
+
for _, node := range errorNodes {
ips := []string{}
var api string
for j, a := range node.peerConfig.GetAllowedIPs() {
- if j == len(node.peerConfig.GetAllowedIPs()) - 1 {
+ if j == len(node.peerConfig.GetAllowedIPs())-1 {
api = a.IP.String()
} else {
ips = append(ips, a.String())
}
}
-
- t = tree.NewTree(tree.NodeString(fmt.Sprintf(`server
- nickname: %v
- e2ee: %v...
- api: %v
- routes: %v
+ nodeString := fmt.Sprintf(
+`server
- error: %v`, node.peerConfig.GetNickname(), node.peerConfig.GetPublicKey().String()[:8], api, strings.Join(ips, ","), errorWrap(node.error, 80))))
+ nickname: %v
+ e2ee: %v...
+ api: %v
+ routes: %v
+
+ error: %v`,
+ node.peerConfig.GetNickname(),
+ node.peerConfig.GetPublicKey().String()[:8],
+ api, strings.Join(ips, ","),
+ errorWrap(node.error, 80),
+ )
+
+ t = tree.NewTree(tree.NodeString(nodeString))
fmt.Fprintln(color.Output, WhiteBold(t))
}
}
diff --git a/src/transport/api/api.go b/src/transport/api/api.go
index 9760cc8..e911b22 100644
--- a/src/transport/api/api.go
+++ b/src/transport/api/api.go
@@ -58,6 +58,11 @@ type NetworkState struct {
ServerRelaySubnet6 netip.Addr
}
+type HostInterface struct {
+ Name string
+ Addrs []net.IPNet
+}
+
type AddAllowedIPsRequest struct {
PublicKey wgtypes.Key
AllowedIPs []net.IPNet
@@ -129,6 +134,7 @@ func Handle(tnet *netstack.Net, devRelay *device.Device, devE2EE *device.Device,
http.HandleFunc("/ping", wrapApi(handlePing()))
http.HandleFunc("/serverinfo", wrapApi(handleServerInfo(configs)))
+ http.HandleFunc("/serverinterfaces", wrapApi(handleServerInterfaces()))
http.HandleFunc("/addpeer", wrapApi(handleAddPeer(devRelay, devE2EE, configs)))
http.HandleFunc("/allocate", wrapApi(handleAllocate(ns)))
http.HandleFunc("/addallowedips", wrapApi(handleAddAllowedIPs(devRelay, configs)))
@@ -189,6 +195,53 @@ func handleServerInfo(configs ServerConfigs) http.HandlerFunc {
}
}
+// handleServerInterfaces responds with network interface information for this server.
+func handleServerInterfaces() http.HandlerFunc {
+ return func(w http.ResponseWriter, r *http.Request) {
+ if r.Method != "GET" {
+ w.WriteHeader(http.StatusMethodNotAllowed)
+ return
+ }
+
+ var interfaces []HostInterface
+ ifs, err := net.Interfaces()
+ if err != nil {
+ log.Printf("API Error: %v", err)
+ }
+
+ for _, ifx := range ifs {
+ newIF := HostInterface{}
+ newIF.Name = ifx.Name
+
+ addrs, err := ifx.Addrs()
+ if err != nil {
+ log.Printf("API Error: %v", err)
+ }
+
+ for _, a := range addrs {
+ _, cidr, err := net.ParseCIDR(a.String())
+ if err != nil {
+ log.Printf("API Error: %v", err)
+ }
+ newIF.Addrs = append(newIF.Addrs, *cidr)
+ }
+
+ interfaces = append(interfaces, newIF)
+ }
+
+ body, err := json.Marshal(interfaces)
+ if err != nil {
+ writeErr(w, err)
+ return
+ }
+
+ _, err = w.Write(body)
+ if err != nil {
+ log.Printf("API Error: %v", err)
+ }
+ }
+}
+
// handleAddPeer adds a peer to either this server's relay or e2ee device.
func handleAddPeer(devRelay *device.Device, devE2EE *device.Device, config ServerConfigs) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
@@ -321,7 +374,7 @@ func handleAllocate(ns *NetworkState) http.HandlerFunc {
}
}
-// handleAddAllowedIPs adds new route to a specfied peer.
+// handleAddAllowedIPs adds new route to a specified peer.
func handleAddAllowedIPs(devRelay *device.Device, config ServerConfigs) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if r.Method != "POST" {