From b565a9faa73d433c30afba762a0ac63baec5bcdc Mon Sep 17 00:00:00 2001 From: James Tucker Date: Thu, 27 Jun 2024 14:35:19 -0700 Subject: [PATCH] cmd/xdpderper: add autodetection for default interface name This makes deployment easier in hetrogenous environments. Updates ENG-4274 Signed-off-by: James Tucker --- cmd/xdpderper/xdpderper.go | 15 ++++- net/netutil/default_interface_portable.go | 64 +++++++++++++++++++ .../default_interface_portable_test.go | 25 ++++++++ 3 files changed, 102 insertions(+), 2 deletions(-) create mode 100644 net/netutil/default_interface_portable.go create mode 100644 net/netutil/default_interface_portable_test.go diff --git a/cmd/xdpderper/xdpderper.go b/cmd/xdpderper/xdpderper.go index 1af9c9d5a..494f65099 100644 --- a/cmd/xdpderper/xdpderper.go +++ b/cmd/xdpderper/xdpderper.go @@ -15,11 +15,12 @@ import ( "github.com/prometheus/client_golang/prometheus" "tailscale.com/derp/xdp" + "tailscale.com/net/netutil" "tailscale.com/tsweb" ) var ( - flagDevice = flag.String("device", "", "target device name") + flagDevice = flag.String("device", "", "target device name (default: autodetect)") flagPort = flag.Int("dst-port", 0, "destination UDP port to serve") flagVerbose = flag.Bool("verbose", false, "verbose output including verifier errors") flagMode = flag.String("mode", "xdp", "XDP mode; valid modes: [xdp, xdpgeneric, xdpdrv, xdpoffload]") @@ -41,8 +42,18 @@ func main() { default: log.Fatal("invalid mode") } + deviceName := *flagDevice + if deviceName == "" { + var err error + deviceName, _, err = netutil.DefaultInterfacePortable() + if err != nil || deviceName == "" { + log.Fatalf("failed to detect default route interface: %v", err) + } + } + log.Printf("binding to device: %s", deviceName) + server, err := xdp.NewSTUNServer(&xdp.STUNServerConfig{ - DeviceName: *flagDevice, + DeviceName: deviceName, DstPort: *flagPort, AttachFlags: attachFlags, FullVerifierErr: *flagVerbose, diff --git a/net/netutil/default_interface_portable.go b/net/netutil/default_interface_portable.go new file mode 100644 index 000000000..d75cefb7a --- /dev/null +++ b/net/netutil/default_interface_portable.go @@ -0,0 +1,64 @@ +// Copyright (c) Tailscale Inc & AUTHORS +// SPDX-License-Identifier: BSD-3-Clause + +package netutil + +import ( + "errors" + "net" + "net/netip" +) + +// DefaultInterfacePortable looks up the current default interface using a portable lookup method that +// works on most systems with a BSD style socket interface. +// +// Returns the interface name and IP address of the default route interface. +// +// If the default cannot be determined, an error is returned. +// Requires that there is a route on the system servicing UDP IPv4. +func DefaultInterfacePortable() (string, netip.Addr, error) { + // Note: UDP dial just performs a connect(2), and doesn't actually send a packet. + c, err := net.Dial("udp4", "8.8.8.8:53") + if err != nil { + return "", netip.Addr{}, err + } + laddr := c.LocalAddr().(*net.UDPAddr) + c.Close() + + ifs, err := net.Interfaces() + if err != nil { + return "", netip.Addr{}, err + } + + var ( + iface *net.Interface + ipnet *net.IPNet + ) + for _, ifc := range ifs { + addrs, err := ifc.Addrs() + if err != nil { + return "", netip.Addr{}, err + } + for _, addr := range addrs { + if ipn, ok := addr.(*net.IPNet); ok { + if ipn.Contains(laddr.IP) { + if ipnet == nil { + ipnet = ipn + iface = &ifc + } else { + newSize, _ := ipn.Mask.Size() + oldSize, _ := ipnet.Mask.Size() + if newSize > oldSize { + ipnet = ipn + iface = &ifc + } + } + } + } + } + } + if iface == nil { + return "", netip.Addr{}, errors.New("no default interface") + } + return iface.Name, laddr.AddrPort().Addr(), nil +} diff --git a/net/netutil/default_interface_portable_test.go b/net/netutil/default_interface_portable_test.go new file mode 100644 index 000000000..03dce3405 --- /dev/null +++ b/net/netutil/default_interface_portable_test.go @@ -0,0 +1,25 @@ +// Copyright (c) Tailscale Inc & AUTHORS +// SPDX-License-Identifier: BSD-3-Clause + +package netutil + +import ( + "testing" +) + +func TestDefaultInterfacePortable(t *testing.T) { + ifName, addr, err := DefaultInterfacePortable() + if err != nil { + t.Fatal(err) + } + + t.Logf("Default interface: %s", ifName) + t.Logf("Default address: %s", addr) + + if ifName == "" { + t.Fatal("Default interface name is empty") + } + if !addr.IsValid() { + t.Fatal("Default address is invalid") + } +}