From c31e29f9a6017ceb815217570b422a015062ab2c Mon Sep 17 00:00:00 2001 From: jochen Date: Sun, 11 Oct 2026 23:51:40 +0200 Subject: [PATCH] Keep watching the tunnel after a failed look, and remember peers' names --- modules/netcheck/cmd/netcheck/tunnel.go | 28 ++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/modules/netcheck/cmd/netcheck/tunnel.go b/modules/netcheck/cmd/netcheck/tunnel.go index b486443f..439b7556 100644 --- a/modules/netcheck/cmd/netcheck/tunnel.go +++ b/modules/netcheck/cmd/netcheck/tunnel.go @@ -20,6 +20,7 @@ import ( "sort" "strconv" "strings" + "sync" "time" ) @@ -109,11 +110,20 @@ func parseDump(out string, now time.Time) (Tunnel, error) { return t, sc.Err() } +// errNoTunnel is a machine without the interface: not a fault, a machine off the private network. +var errNoTunnel = errors.New("this machine has no " + TunnelInterface) + +// names remembers the mesh's name of each peer address once found, so the watcher asks no resolver every look. +var names sync.Map + // readTunnel asks wg for the interface's state and names each peer by the mesh's names. func readTunnel(ctx context.Context) (Tunnel, error) { out, err := exec.CommandContext(ctx, "wg", "show", TunnelInterface, "dump").Output() if err != nil { var ee *exec.ExitError + if errors.As(err, &ee) && strings.Contains(string(ee.Stderr), "No such device") { + return Tunnel{}, errNoTunnel + } if errors.As(err, &ee) && len(ee.Stderr) > 0 { return Tunnel{}, fmt.Errorf("wg show %s: %s", TunnelInterface, strings.TrimSpace(string(ee.Stderr))) } @@ -124,7 +134,15 @@ func readTunnel(ctx context.Context) (Tunnel, error) { return t, err } for i := range t.Peers { - t.Peers[i].Name = nameOf(ctx, t.Peers[i].AllowedIPs) + k := peerKey(t.Peers[i]) + if known, ok := names.Load(k); ok { + t.Peers[i].Name = known.(string) + continue + } + if name := nameOf(ctx, t.Peers[i].AllowedIPs); name != "" { + names.Store(k, name) + t.Peers[i].Name = name + } } return t, nil } @@ -204,7 +222,7 @@ func report(err error) { } // watchTunnel looks every 20 seconds while this machine has the interface; a machine without it is -// looked at again every ten minutes, saying so once. +// looked at again every ten minutes. A failed look is said once and tried again at the next look. func watchTunnel(emit emitter) { var w tunnelWatch said := "" @@ -217,7 +235,11 @@ func watchTunnel(emit emitter) { say("not watching the tunnel: %v", err) said = err.Error() } - time.Sleep(10 * time.Minute) + if errors.Is(err, errNoTunnel) { + time.Sleep(10 * time.Minute) + } else { + time.Sleep(20 * time.Second) + } continue } said = ""