From af4b826b680cb128b9366901b4b1c67fb7a8c68e Mon Sep 17 00:00:00 2001 From: Cuong Manh Le Date: Fri, 7 Feb 2025 19:09:20 +0700 Subject: [PATCH] cmd/cli: implement valid interfaces map for all systems Previously, a valid interfaces map is only meaningful on Windows and Darwin, where ctrld needs to set DNS for all physical interfaces. With new network monitor, the valid interfaces is used for checking new changes, thus we have to implement the valid interfaces map for all systems. - On Linux, just retrieving all non-virtual interfaces. - On others, fallback to use default route interface only. --- cmd/cli/dns_proxy.go | 2 +- cmd/cli/net_linux.go | 52 +++++++++++++++++++++++++++++++++++++++++++ cmd/cli/net_others.go | 17 +++++++++++--- 3 files changed, 67 insertions(+), 4 deletions(-) create mode 100644 cmd/cli/net_linux.go diff --git a/cmd/cli/dns_proxy.go b/cmd/cli/dns_proxy.go index d9f124e..0447eef 100644 --- a/cmd/cli/dns_proxy.go +++ b/cmd/cli/dns_proxy.go @@ -1366,7 +1366,7 @@ func (p *prog) monitorNetworkChanges(ctx context.Context) error { changed := false activeInterfaceExists := false - changeIPs := []netip.Prefix{} + var changeIPs []netip.Prefix // Check each valid interface for changes for ifaceName := range validIfaces { oldIface := delta.Old.Interface[ifaceName] diff --git a/cmd/cli/net_linux.go b/cmd/cli/net_linux.go new file mode 100644 index 0000000..ea17d3d --- /dev/null +++ b/cmd/cli/net_linux.go @@ -0,0 +1,52 @@ +package cli + +import ( + "net" + "net/netip" + "os" + "strings" + + "tailscale.com/net/netmon" +) + +func patchNetIfaceName(iface *net.Interface) (bool, error) { return true, nil } + +// validInterface reports whether the *net.Interface is a valid one. +// Only non-virtual interfaces are considered valid. +func validInterface(iface *net.Interface, validIfacesMap map[string]struct{}) bool { + _, ok := validIfacesMap[iface.Name] + return ok +} + +// validInterfacesMap returns a set containing non virtual interfaces. +func validInterfacesMap() map[string]struct{} { + m := make(map[string]struct{}) + vis := virtualInterfaces() + netmon.ForeachInterface(func(i netmon.Interface, prefixes []netip.Prefix) { + if _, existed := vis[i.Name]; existed { + return + } + m[i.Name] = struct{}{} + }) + // Fallback to default route interface if found nothing. + if len(m) == 0 { + defaultRoute, err := netmon.DefaultRoute() + if err != nil { + return m + } + m[defaultRoute.InterfaceName] = struct{}{} + } + return m +} + +// virtualInterfaces returns a map of virtual interfaces on current machine. +func virtualInterfaces() map[string]struct{} { + s := make(map[string]struct{}) + entries, _ := os.ReadDir("/sys/devices/virtual/net") + for _, entry := range entries { + if entry.IsDir() { + s[strings.TrimSpace(entry.Name())] = struct{}{} + } + } + return s +} diff --git a/cmd/cli/net_others.go b/cmd/cli/net_others.go index edd89ec..f347278 100644 --- a/cmd/cli/net_others.go +++ b/cmd/cli/net_others.go @@ -1,11 +1,22 @@ -//go:build !darwin && !windows +//go:build !darwin && !windows && !linux package cli -import "net" +import ( + "net" + + "tailscale.com/net/netmon" +) func patchNetIfaceName(iface *net.Interface) (bool, error) { return true, nil } func validInterface(iface *net.Interface, validIfacesMap map[string]struct{}) bool { return true } -func validInterfacesMap() map[string]struct{} { return nil } +// validInterfacesMap returns a set containing only default route interfaces. +func validInterfacesMap() map[string]struct{} { + defaultRoute, err := netmon.DefaultRoute() + if err != nil { + return nil + } + return map[string]struct{}{defaultRoute.InterfaceName: {}} +}