From d6abbc2e610f605cdb114f97182e14d54794dc87 Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Wed, 22 Jan 2025 18:07:57 -0800 Subject: [PATCH] net/tstun: move TAP support out to separate package feature/tap Still behind the same ts_omit_tap build tag. See #14738 for background on the pattern. Updates #12614 Change-Id: I03fb3d2bf137111e727415bd8e713d8568156ecc Signed-off-by: Brad Fitzpatrick --- cmd/k8s-operator/depaware.txt | 7 +++-- cmd/tailscaled/depaware.txt | 7 +++-- feature/condregister/maybe_tap.go | 8 +++++ feature/feature.go | 39 +++++++++++++++++++++++++ {net/tstun => feature/tap}/tap_linux.go | 24 +++++++++++---- net/tstun/tun.go | 9 +++--- net/tstun/wrap.go | 11 ++++--- 7 files changed, 85 insertions(+), 20 deletions(-) create mode 100644 feature/condregister/maybe_tap.go rename {net/tstun => feature/tap}/tap_linux.go (95%) diff --git a/cmd/k8s-operator/depaware.txt b/cmd/k8s-operator/depaware.txt index bdcf3417a..11a9201d4 100644 --- a/cmd/k8s-operator/depaware.txt +++ b/cmd/k8s-operator/depaware.txt @@ -140,7 +140,7 @@ tailscale.com/cmd/k8s-operator dependencies: (generated by github.com/tailscale/ github.com/gorilla/securecookie from github.com/gorilla/csrf github.com/hdevalence/ed25519consensus from tailscale.com/clientupdate/distsign+ L 💣 github.com/illarion/gonotify/v2 from tailscale.com/net/dns - L github.com/insomniacslk/dhcp/dhcpv4 from tailscale.com/net/tstun + L github.com/insomniacslk/dhcp/dhcpv4 from tailscale.com/feature/tap L github.com/insomniacslk/dhcp/iana from github.com/insomniacslk/dhcp/dhcpv4 L github.com/insomniacslk/dhcp/interfaces from github.com/insomniacslk/dhcp/dhcpv4 L github.com/insomniacslk/dhcp/rfc1035label from github.com/insomniacslk/dhcp/dhcpv4 @@ -302,7 +302,7 @@ tailscale.com/cmd/k8s-operator dependencies: (generated by github.com/tailscale/ gvisor.dev/gvisor/pkg/tcpip/network/internal/fragmentation from gvisor.dev/gvisor/pkg/tcpip/network/ipv4+ gvisor.dev/gvisor/pkg/tcpip/network/internal/ip from gvisor.dev/gvisor/pkg/tcpip/network/ipv4+ gvisor.dev/gvisor/pkg/tcpip/network/internal/multicast from gvisor.dev/gvisor/pkg/tcpip/network/ipv4+ - gvisor.dev/gvisor/pkg/tcpip/network/ipv4 from tailscale.com/net/tstun+ + gvisor.dev/gvisor/pkg/tcpip/network/ipv4 from tailscale.com/feature/tap+ gvisor.dev/gvisor/pkg/tcpip/network/ipv6 from tailscale.com/wgengine/netstack+ gvisor.dev/gvisor/pkg/tcpip/ports from gvisor.dev/gvisor/pkg/tcpip/stack+ gvisor.dev/gvisor/pkg/tcpip/seqnum from gvisor.dev/gvisor/pkg/tcpip/header+ @@ -801,8 +801,9 @@ tailscale.com/cmd/k8s-operator dependencies: (generated by github.com/tailscale/ tailscale.com/drive from tailscale.com/client/tailscale+ tailscale.com/envknob from tailscale.com/client/tailscale+ tailscale.com/envknob/featureknob from tailscale.com/client/web+ - tailscale.com/feature from tailscale.com/feature/wakeonlan + tailscale.com/feature from tailscale.com/feature/wakeonlan+ tailscale.com/feature/condregister from tailscale.com/tsnet + L tailscale.com/feature/tap from tailscale.com/feature/condregister tailscale.com/feature/wakeonlan from tailscale.com/feature/condregister tailscale.com/health from tailscale.com/control/controlclient+ tailscale.com/health/healthmsg from tailscale.com/ipn/ipnlocal diff --git a/cmd/tailscaled/depaware.txt b/cmd/tailscaled/depaware.txt index 5246b82b9..4f81d93dd 100644 --- a/cmd/tailscaled/depaware.txt +++ b/cmd/tailscaled/depaware.txt @@ -112,7 +112,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de github.com/gorilla/securecookie from github.com/gorilla/csrf github.com/hdevalence/ed25519consensus from tailscale.com/clientupdate/distsign+ L 💣 github.com/illarion/gonotify/v2 from tailscale.com/net/dns - L github.com/insomniacslk/dhcp/dhcpv4 from tailscale.com/net/tstun + L github.com/insomniacslk/dhcp/dhcpv4 from tailscale.com/feature/tap L github.com/insomniacslk/dhcp/iana from github.com/insomniacslk/dhcp/dhcpv4 L github.com/insomniacslk/dhcp/interfaces from github.com/insomniacslk/dhcp/dhcpv4 L github.com/insomniacslk/dhcp/rfc1035label from github.com/insomniacslk/dhcp/dhcpv4 @@ -214,7 +214,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de gvisor.dev/gvisor/pkg/tcpip/network/internal/fragmentation from gvisor.dev/gvisor/pkg/tcpip/network/ipv4+ gvisor.dev/gvisor/pkg/tcpip/network/internal/ip from gvisor.dev/gvisor/pkg/tcpip/network/ipv4+ gvisor.dev/gvisor/pkg/tcpip/network/internal/multicast from gvisor.dev/gvisor/pkg/tcpip/network/ipv4+ - gvisor.dev/gvisor/pkg/tcpip/network/ipv4 from tailscale.com/net/tstun+ + gvisor.dev/gvisor/pkg/tcpip/network/ipv4 from tailscale.com/feature/tap+ gvisor.dev/gvisor/pkg/tcpip/network/ipv6 from tailscale.com/wgengine/netstack+ gvisor.dev/gvisor/pkg/tcpip/ports from gvisor.dev/gvisor/pkg/tcpip/stack+ gvisor.dev/gvisor/pkg/tcpip/seqnum from gvisor.dev/gvisor/pkg/tcpip/header+ @@ -259,8 +259,9 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de tailscale.com/drive/driveimpl/shared from tailscale.com/drive/driveimpl+ tailscale.com/envknob from tailscale.com/client/tailscale+ tailscale.com/envknob/featureknob from tailscale.com/client/web+ - tailscale.com/feature from tailscale.com/feature/wakeonlan + tailscale.com/feature from tailscale.com/feature/wakeonlan+ tailscale.com/feature/condregister from tailscale.com/cmd/tailscaled + L tailscale.com/feature/tap from tailscale.com/feature/condregister tailscale.com/feature/wakeonlan from tailscale.com/feature/condregister tailscale.com/health from tailscale.com/control/controlclient+ tailscale.com/health/healthmsg from tailscale.com/ipn/ipnlocal diff --git a/feature/condregister/maybe_tap.go b/feature/condregister/maybe_tap.go new file mode 100644 index 000000000..eca4fc3ac --- /dev/null +++ b/feature/condregister/maybe_tap.go @@ -0,0 +1,8 @@ +// Copyright (c) Tailscale Inc & AUTHORS +// SPDX-License-Identifier: BSD-3-Clause + +//go:build linux && !ts_omit_tap + +package condregister + +import _ "tailscale.com/feature/tap" diff --git a/feature/feature.go b/feature/feature.go index ea290c43a..6415cfc4a 100644 --- a/feature/feature.go +++ b/feature/feature.go @@ -4,6 +4,8 @@ // Package feature tracks which features are linked into the binary. package feature +import "reflect" + var in = map[string]bool{} // Register notes that the named feature is linked into the binary. @@ -13,3 +15,40 @@ func Register(name string) { } in[name] = true } + +// Hook is a func that can only be set once. +// +// It is not safe for concurrent use. +type Hook[Func any] struct { + f Func + ok bool +} + +// IsSet reports whether the hook has been set. +func (h *Hook[Func]) IsSet() bool { + return h.ok +} + +// Set sets the hook function, panicking if it's already been set +// or f is the zero value. +// +// It's meant to be called in init. +func (h *Hook[Func]) Set(f Func) { + if h.ok { + panic("Set on already-set feature hook") + } + if reflect.ValueOf(f).IsZero() { + panic("Set with zero value") + } + h.f = f + h.ok = true +} + +// Get returns the hook function, or panics if it hasn't been set. +// Use IsSet to check if it's been set. +func (h *Hook[Func]) Get() Func { + if !h.ok { + panic("Get on unset feature hook, without IsSet") + } + return h.f +} diff --git a/net/tstun/tap_linux.go b/feature/tap/tap_linux.go similarity index 95% rename from net/tstun/tap_linux.go rename to feature/tap/tap_linux.go index 8a00a9692..58ac00593 100644 --- a/net/tstun/tap_linux.go +++ b/feature/tap/tap_linux.go @@ -1,9 +1,8 @@ // Copyright (c) Tailscale Inc & AUTHORS // SPDX-License-Identifier: BSD-3-Clause -//go:build !ts_omit_tap - -package tstun +// Package tap registers Tailscale's experimental (demo) Linux TAP (Layer 2) support. +package tap import ( "bytes" @@ -26,6 +25,7 @@ import ( "tailscale.com/net/netaddr" "tailscale.com/net/packet" "tailscale.com/net/tsaddr" + "tailscale.com/net/tstun" "tailscale.com/syncs" "tailscale.com/types/ipproto" "tailscale.com/types/logger" @@ -38,7 +38,11 @@ import ( // For now just hard code it. var ourMAC = net.HardwareAddr{0x30, 0x2D, 0x66, 0xEC, 0x7A, 0x93} -func init() { createTAP = createTAPLinux } +const tapDebug = tstun.TAPDebug + +func init() { + tstun.CreateTAP.Set(createTAPLinux) +} func createTAPLinux(logf logger.Logf, tapName, bridgeName string) (tun.Device, error) { fd, err := unix.Open("/dev/net/tun", unix.O_RDWR, 0) @@ -87,7 +91,10 @@ var ( etherTypeIPv6 = etherType{0x86, 0xDD} ) -const ipv4HeaderLen = 20 +const ( + ipv4HeaderLen = 20 + ethernetFrameSize = 14 // 2 six byte MACs, 2 bytes ethertype +) const ( consumePacket = true @@ -186,6 +193,11 @@ var ( cgnatNetMask = net.IPMask(net.ParseIP("255.192.0.0").To4()) ) +// parsedPacketPool holds a pool of Parsed structs for use in filtering. +// This is needed because escape analysis cannot see that parsed packets +// do not escape through {Pre,Post}Filter{In,Out}. +var parsedPacketPool = sync.Pool{New: func() any { return new(packet.Parsed) }} + // handleDHCPRequest handles receiving a raw TAP ethernet frame and reports whether // it's been handled as a DHCP request. That is, it reports whether the frame should // be ignored by the caller and not passed on. @@ -392,7 +404,7 @@ type tapDevice struct { destMACAtomic syncs.AtomicValue[[6]byte] } -var _ setIPer = (*tapDevice)(nil) +var _ tstun.SetIPer = (*tapDevice)(nil) func (t *tapDevice) SetIP(ipV4, ipV6TODO netip.Addr) error { t.clientIPv4.Store(ipV4.String()) diff --git a/net/tstun/tun.go b/net/tstun/tun.go index 56c66c83a..44ccdfc99 100644 --- a/net/tstun/tun.go +++ b/net/tstun/tun.go @@ -14,11 +14,12 @@ import ( "time" "github.com/tailscale/wireguard-go/tun" + "tailscale.com/feature" "tailscale.com/types/logger" ) -// createTAP is non-nil on Linux. -var createTAP func(logf logger.Logf, tapName, bridgeName string) (tun.Device, error) +// CrateTAP is the hook set by feature/tap. +var CreateTAP feature.Hook[func(logf logger.Logf, tapName, bridgeName string) (tun.Device, error)] // New returns a tun.Device for the requested device name, along with // the OS-dependent name that was allocated to the device. @@ -29,7 +30,7 @@ func New(logf logger.Logf, tunName string) (tun.Device, string, error) { if runtime.GOOS != "linux" { return nil, "", errors.New("tap only works on Linux") } - if createTAP == nil { // if the ts_omit_tap tag is used + if !CreateTAP.IsSet() { // if the ts_omit_tap tag is used return nil, "", errors.New("tap is not supported in this build") } f := strings.Split(tunName, ":") @@ -42,7 +43,7 @@ func New(logf logger.Logf, tunName string) (tun.Device, string, error) { default: return nil, "", errors.New("bogus tap argument") } - dev, err = createTAP(logf, tapName, bridgeName) + dev, err = CreateTAP.Get()(logf, tapName, bridgeName) } else { dev, err = tun.CreateTUN(tunName, int(DefaultTUNMTU())) } diff --git a/net/tstun/wrap.go b/net/tstun/wrap.go index e4ff36b49..2d56f3768 100644 --- a/net/tstun/wrap.go +++ b/net/tstun/wrap.go @@ -53,7 +53,8 @@ const PacketStartOffset = device.MessageTransportHeaderSize // of a packet that can be injected into a tstun.Wrapper. const MaxPacketSize = device.MaxContentSize -const tapDebug = false // for super verbose TAP debugging +// TAPDebug is whether super verbose TAP debugging is enabled. +const TAPDebug = false var ( // ErrClosed is returned when attempting an operation on a closed Wrapper. @@ -459,7 +460,7 @@ func (t *Wrapper) pollVector() { return } n, err = reader(t.vectorBuffer[:], sizes, readOffset) - if t.isTAP && tapDebug { + if t.isTAP && TAPDebug { s := fmt.Sprintf("% x", t.vectorBuffer[0][:]) for strings.HasSuffix(s, " 00") { s = strings.TrimSuffix(s, " 00") @@ -792,7 +793,9 @@ func (pc *peerConfigTable) outboundPacketIsJailed(p *packet.Parsed) bool { return c.jailed } -type setIPer interface { +// SetIPer is the interface expected to be implemented by the TAP implementation +// of tun.Device. +type SetIPer interface { // SetIP sets the IP addresses of the TAP device. SetIP(ipV4, ipV6 netip.Addr) error } @@ -800,7 +803,7 @@ type setIPer interface { // SetWGConfig is called when a new NetworkMap is received. func (t *Wrapper) SetWGConfig(wcfg *wgcfg.Config) { if t.isTAP { - if sip, ok := t.tdev.(setIPer); ok { + if sip, ok := t.tdev.(SetIPer); ok { sip.SetIP(findV4(wcfg.Addresses), findV6(wcfg.Addresses)) } }