Skip to content

Commit ae063c0

Browse files
authored
Server Network Interface Enumeration (#69)
Adds the --network-info (-n) boolean flag to the status command. For reachable servers, this displays a list of network interface names and associated CIDRs on the server host.
1 parent a886c71 commit ae063c0

File tree

4 files changed

+144
-20
lines changed

4 files changed

+144
-20
lines changed

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,9 @@ Once the client configs have been imported and Wireguard is started, you can vie
294294
╰─────────────────────╯
295295
```
296296

297+
> [!TIP]
298+
> Add the `--network-info` flag to this command to get a list of each Server host's network interfaces and associated CIDR addresses.
299+
297300
## Add Server (Optional)
298301

299302
<div align="center">

src/api/api.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ type request struct {
2626
Body []byte
2727
}
2828

29+
// Re-export the server api struct so files importing this package can access it
30+
type HostInterface serverapi.HostInterface
31+
2932
// MakeRequest attempts to send an API query to the Wiretap server.
3033
func makeRequest(req request) ([]byte, error) {
3134
// Never use a proxy for API requests, they must go direct through the Wiretap network
@@ -93,6 +96,24 @@ func ServerInfo(apiAddr netip.AddrPort) (peer.Config, peer.Config, error) {
9396
return *configs.RelayConfig, *configs.E2EEConfig, nil
9497
}
9598

99+
func ServerInterfaces(apiAddr netip.AddrPort) ([]HostInterface, error) {
100+
body, err := makeRequest(request{
101+
URL: makeUrl(apiAddr, "serverinterfaces", []string{}),
102+
Method: "GET",
103+
})
104+
if err != nil {
105+
return nil, err
106+
}
107+
108+
var interfaces []HostInterface
109+
err = json.Unmarshal(body, &interfaces)
110+
if err != nil {
111+
return nil, err
112+
}
113+
114+
return interfaces, nil
115+
}
116+
96117
func AllocateNode(apiAddr netip.AddrPort, peerType peer.PeerType) (serverapi.NetworkState, error) {
97118
body, err := makeRequest(request{
98119
URL: makeUrl(apiAddr, "allocate", []string{fmt.Sprintf("type=%d", peerType)}),

src/cmd/status.go

Lines changed: 66 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,15 @@ import (
1414
)
1515

1616
type statusCmdConfig struct {
17+
networkInfo bool
1718
configFileRelay string
1819
configFileE2EE string
1920
}
2021

2122
// Defaults for status command.
2223
// See root command for shared defaults.
2324
var statusCmd = statusCmdConfig{
25+
networkInfo: false,
2426
configFileRelay: ConfigRelay,
2527
configFileE2EE: ConfigE2EE,
2628
}
@@ -39,29 +41,31 @@ func init() {
3941

4042
rootCmd.AddCommand(cmd)
4143

44+
cmd.Flags().BoolVarP(&statusCmd.networkInfo, "network-info", "n", statusCmd.networkInfo, "Display network info for each online server node")
4245
cmd.Flags().StringVarP(&statusCmd.configFileRelay, "relay", "1", statusCmd.configFileRelay, "wireguard relay config input filename")
4346
cmd.Flags().StringVarP(&statusCmd.configFileE2EE, "e2ee", "2", statusCmd.configFileE2EE, "wireguard E2EE config input filename")
4447

4548
cmd.Flags().SortFlags = false
4649
}
4750

4851
// Run attempts to parse config files into a network diagram.
49-
func (c statusCmdConfig) Run() {
52+
func (cc statusCmdConfig) Run() {
5053
// Start building tree.
5154
type Node struct {
5255
peerConfig peer.PeerConfig
5356
relayConfig peer.Config
5457
e2eeConfig peer.Config
5558
children []*Node
59+
interfaces []api.HostInterface
5660
error string
5761
}
5862

5963
var err error
6064

6165
// Parse the relay and e2ee config files
62-
clientConfigRelay, err := peer.ParseConfig(c.configFileRelay)
66+
clientConfigRelay, err := peer.ParseConfig(cc.configFileRelay)
6367
check("failed to parse relay config file", err)
64-
clientConfigE2EE, err := peer.ParseConfig(c.configFileE2EE)
68+
clientConfigE2EE, err := peer.ParseConfig(cc.configFileE2EE)
6569
check("failed to parse e2ee config file", err)
6670

6771
client := Node{
@@ -81,15 +85,26 @@ func (c statusCmdConfig) Run() {
8185
relayConfig, e2eeConfig, err := api.ServerInfo(netip.AddrPortFrom(ep.GetApiAddr(), uint16(ApiPort)))
8286
if err != nil {
8387
errorNodes = append(errorNodes, Node{
84-
peerConfig: ep,
85-
error: err.Error(),
88+
peerConfig: ep,
89+
error: err.Error(),
8690
})
87-
91+
8892
} else {
93+
var interfaces []api.HostInterface
94+
if cc.networkInfo {
95+
interfaces, err = api.ServerInterfaces(netip.AddrPortFrom(ep.GetApiAddr(), uint16(ApiPort)))
96+
if err != nil {
97+
interfaces = append(interfaces, api.HostInterface{
98+
Name: "ERROR: " + err.Error(),
99+
})
100+
}
101+
}
102+
89103
nodes[relayConfig.GetPublicKey()] = Node{
90104
peerConfig: ep,
91105
relayConfig: relayConfig,
92106
e2eeConfig: e2eeConfig,
107+
interfaces: interfaces,
93108
}
94109
}
95110
}
@@ -131,19 +146,43 @@ func (c statusCmdConfig) Run() {
131146
ips := []string{}
132147
var api string
133148
for j, a := range c.peerConfig.GetAllowedIPs() {
134-
if j == len(c.peerConfig.GetAllowedIPs()) - 1 {
149+
if j == len(c.peerConfig.GetAllowedIPs())-1 {
135150
api = a.IP.String()
136151
} else {
137152
ips = append(ips, a.String())
138153
}
139154
}
140-
t.AddChild(tree.NodeString(fmt.Sprintf(`server
155+
156+
nodeString := fmt.Sprintf(
157+
`server
141158
nickname: %v
142159
relay: %v...
143160
e2ee: %v...
144161
145162
api: %v
146-
routes: %v `, c.peerConfig.GetNickname(), c.relayConfig.GetPublicKey()[:8], c.e2eeConfig.GetPublicKey()[:8], api, strings.Join(ips, ","))))
163+
routes: %v `,
164+
c.peerConfig.GetNickname(),
165+
c.relayConfig.GetPublicKey()[:8],
166+
c.e2eeConfig.GetPublicKey()[:8],
167+
api,
168+
strings.Join(ips, ","),
169+
)
170+
171+
if cc.networkInfo {
172+
nodeString += `
173+
174+
Network Interfaces:
175+
-------------------
176+
`
177+
for _, ifx := range c.interfaces {
178+
nodeString += fmt.Sprintf("%v:\n", ifx.Name)
179+
for _, a := range ifx.Addrs {
180+
nodeString += strings.Repeat(" ", 2) + a.String() + "\n"
181+
}
182+
}
183+
}
184+
185+
t.AddChild(tree.NodeString(nodeString))
147186
child, err := t.Child(0)
148187
check("could not build tree", err)
149188
treeTraversal(node.children[i], child)
@@ -156,31 +195,39 @@ func (c statusCmdConfig) Run() {
156195
fmt.Println()
157196
fmt.Fprintln(color.Output, WhiteBold(t))
158197
fmt.Println()
159-
198+
160199
if len(errorNodes) > 0 {
161200
// Display known peers that we had issues connecting to
162201
fmt.Fprintln(color.Output, WhiteBold("Peers with Errors:"))
163202
fmt.Println()
164-
203+
165204
for _, node := range errorNodes {
166205
ips := []string{}
167206
var api string
168207
for j, a := range node.peerConfig.GetAllowedIPs() {
169-
if j == len(node.peerConfig.GetAllowedIPs()) - 1 {
208+
if j == len(node.peerConfig.GetAllowedIPs())-1 {
170209
api = a.IP.String()
171210
} else {
172211
ips = append(ips, a.String())
173212
}
174213
}
175-
176-
t = tree.NewTree(tree.NodeString(fmt.Sprintf(`server
177214

178-
nickname: %v
179-
e2ee: %v...
180-
api: %v
181-
routes: %v
215+
nodeString := fmt.Sprintf(
216+
`server
182217
183-
error: %v`, node.peerConfig.GetNickname(), node.peerConfig.GetPublicKey().String()[:8], api, strings.Join(ips, ","), errorWrap(node.error, 80))))
218+
nickname: %v
219+
e2ee: %v...
220+
api: %v
221+
routes: %v
222+
223+
error: %v`,
224+
node.peerConfig.GetNickname(),
225+
node.peerConfig.GetPublicKey().String()[:8],
226+
api, strings.Join(ips, ","),
227+
errorWrap(node.error, 80),
228+
)
229+
230+
t = tree.NewTree(tree.NodeString(nodeString))
184231
fmt.Fprintln(color.Output, WhiteBold(t))
185232
}
186233
}

src/transport/api/api.go

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,11 @@ type NetworkState struct {
5858
ServerRelaySubnet6 netip.Addr
5959
}
6060

61+
type HostInterface struct {
62+
Name string
63+
Addrs []net.IPNet
64+
}
65+
6166
type AddAllowedIPsRequest struct {
6267
PublicKey wgtypes.Key
6368
AllowedIPs []net.IPNet
@@ -129,6 +134,7 @@ func Handle(tnet *netstack.Net, devRelay *device.Device, devE2EE *device.Device,
129134

130135
http.HandleFunc("/ping", wrapApi(handlePing()))
131136
http.HandleFunc("/serverinfo", wrapApi(handleServerInfo(configs)))
137+
http.HandleFunc("/serverinterfaces", wrapApi(handleServerInterfaces()))
132138
http.HandleFunc("/addpeer", wrapApi(handleAddPeer(devRelay, devE2EE, configs)))
133139
http.HandleFunc("/allocate", wrapApi(handleAllocate(ns)))
134140
http.HandleFunc("/addallowedips", wrapApi(handleAddAllowedIPs(devRelay, configs)))
@@ -189,6 +195,53 @@ func handleServerInfo(configs ServerConfigs) http.HandlerFunc {
189195
}
190196
}
191197

198+
// handleServerInterfaces responds with network interface information for this server.
199+
func handleServerInterfaces() http.HandlerFunc {
200+
return func(w http.ResponseWriter, r *http.Request) {
201+
if r.Method != "GET" {
202+
w.WriteHeader(http.StatusMethodNotAllowed)
203+
return
204+
}
205+
206+
var interfaces []HostInterface
207+
ifs, err := net.Interfaces()
208+
if err != nil {
209+
log.Printf("API Error: %v", err)
210+
}
211+
212+
for _, ifx := range ifs {
213+
newIF := HostInterface{}
214+
newIF.Name = ifx.Name
215+
216+
addrs, err := ifx.Addrs()
217+
if err != nil {
218+
log.Printf("API Error: %v", err)
219+
}
220+
221+
for _, a := range addrs {
222+
_, cidr, err := net.ParseCIDR(a.String())
223+
if err != nil {
224+
log.Printf("API Error: %v", err)
225+
}
226+
newIF.Addrs = append(newIF.Addrs, *cidr)
227+
}
228+
229+
interfaces = append(interfaces, newIF)
230+
}
231+
232+
body, err := json.Marshal(interfaces)
233+
if err != nil {
234+
writeErr(w, err)
235+
return
236+
}
237+
238+
_, err = w.Write(body)
239+
if err != nil {
240+
log.Printf("API Error: %v", err)
241+
}
242+
}
243+
}
244+
192245
// handleAddPeer adds a peer to either this server's relay or e2ee device.
193246
func handleAddPeer(devRelay *device.Device, devE2EE *device.Device, config ServerConfigs) http.HandlerFunc {
194247
return func(w http.ResponseWriter, r *http.Request) {
@@ -321,7 +374,7 @@ func handleAllocate(ns *NetworkState) http.HandlerFunc {
321374
}
322375
}
323376

324-
// handleAddAllowedIPs adds new route to a specfied peer.
377+
// handleAddAllowedIPs adds new route to a specified peer.
325378
func handleAddAllowedIPs(devRelay *device.Device, config ServerConfigs) http.HandlerFunc {
326379
return func(w http.ResponseWriter, r *http.Request) {
327380
if r.Method != "POST" {

0 commit comments

Comments
 (0)