From 3c7e35167133003531d217e9597fd9e6477fc3d3 Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Sat, 4 Oct 2025 11:43:17 -0700 Subject: [PATCH] net/connstats: make it modular (omittable) Saves only 12 KB, but notably removes some deps on packages that future changes can then eliminate entirely. Updates #12614 Change-Id: Ibf830d3ee08f621d0a2011b1d4cd175427ef50df Signed-off-by: Brad Fitzpatrick --- cmd/tailscaled/depaware-min.txt | 3 +-- cmd/tailscaled/depaware-minbox.txt | 3 +-- cmd/tailscaled/deps_test.go | 1 + .../feature_connstats_disabled.go | 13 ++++++++++ .../feature_connstats_enabled.go | 13 ++++++++++ feature/featuretags/featuretags.go | 6 ++++- net/connstats/stats.go | 2 ++ net/connstats/stats_omit.go | 24 +++++++++++++++++ net/tstun/wrap.go | 26 ++++++++++++------- wgengine/magicsock/magicsock.go | 10 ++++--- wgengine/netlog/netlog.go | 21 +++++++++------ 11 files changed, 97 insertions(+), 25 deletions(-) create mode 100644 feature/buildfeatures/feature_connstats_disabled.go create mode 100644 feature/buildfeatures/feature_connstats_enabled.go create mode 100644 net/connstats/stats_omit.go diff --git a/cmd/tailscaled/depaware-min.txt b/cmd/tailscaled/depaware-min.txt index 9210b4377..6ed602dc1 100644 --- a/cmd/tailscaled/depaware-min.txt +++ b/cmd/tailscaled/depaware-min.txt @@ -134,7 +134,6 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de tailscale.com/types/logger from tailscale.com/appc+ tailscale.com/types/logid from tailscale.com/cmd/tailscaled+ tailscale.com/types/mapx from tailscale.com/ipn/ipnext - tailscale.com/types/netlogtype from tailscale.com/net/connstats tailscale.com/types/netmap from tailscale.com/control/controlclient+ tailscale.com/types/nettype from tailscale.com/net/batching+ tailscale.com/types/opt from tailscale.com/control/controlknobs+ @@ -217,7 +216,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de golang.org/x/net/internal/socket from golang.org/x/net/icmp+ golang.org/x/net/ipv4 from github.com/tailscale/wireguard-go/conn+ golang.org/x/net/ipv6 from github.com/tailscale/wireguard-go/conn+ - golang.org/x/sync/errgroup from github.com/mdlayher/socket+ + golang.org/x/sync/errgroup from github.com/mdlayher/socket golang.org/x/sys/cpu from github.com/tailscale/wireguard-go/tun+ golang.org/x/sys/unix from github.com/jsimonetti/rtnetlink/internal/unix+ golang.org/x/term from tailscale.com/logpolicy diff --git a/cmd/tailscaled/depaware-minbox.txt b/cmd/tailscaled/depaware-minbox.txt index b183609f3..70fed796f 100644 --- a/cmd/tailscaled/depaware-minbox.txt +++ b/cmd/tailscaled/depaware-minbox.txt @@ -160,7 +160,6 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de tailscale.com/types/logger from tailscale.com/appc+ tailscale.com/types/logid from tailscale.com/cmd/tailscaled+ tailscale.com/types/mapx from tailscale.com/ipn/ipnext - tailscale.com/types/netlogtype from tailscale.com/net/connstats tailscale.com/types/netmap from tailscale.com/control/controlclient+ tailscale.com/types/nettype from tailscale.com/net/batching+ tailscale.com/types/opt from tailscale.com/control/controlknobs+ @@ -245,7 +244,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de golang.org/x/net/internal/socket from golang.org/x/net/icmp+ golang.org/x/net/ipv4 from github.com/tailscale/wireguard-go/conn+ golang.org/x/net/ipv6 from github.com/tailscale/wireguard-go/conn+ - golang.org/x/sync/errgroup from github.com/mdlayher/socket+ + golang.org/x/sync/errgroup from github.com/mdlayher/socket golang.org/x/sys/cpu from github.com/tailscale/wireguard-go/tun+ golang.org/x/sys/unix from github.com/jsimonetti/rtnetlink/internal/unix+ golang.org/x/term from tailscale.com/logpolicy diff --git a/cmd/tailscaled/deps_test.go b/cmd/tailscaled/deps_test.go index c54f014f6..2dd140f23 100644 --- a/cmd/tailscaled/deps_test.go +++ b/cmd/tailscaled/deps_test.go @@ -243,6 +243,7 @@ func TestMinTailscaledNoCLI(t *testing.T) { "golang.org/x/net/proxy", "internal/socks", "github.com/tailscale/peercred", + "tailscale.com/types/netlogtype", } deptest.DepChecker{ GOOS: "linux", diff --git a/feature/buildfeatures/feature_connstats_disabled.go b/feature/buildfeatures/feature_connstats_disabled.go new file mode 100644 index 000000000..d9aac0e80 --- /dev/null +++ b/feature/buildfeatures/feature_connstats_disabled.go @@ -0,0 +1,13 @@ +// Copyright (c) Tailscale Inc & AUTHORS +// SPDX-License-Identifier: BSD-3-Clause + +// Code generated by gen.go; DO NOT EDIT. + +//go:build ts_omit_connstats + +package buildfeatures + +// HasConnStats is whether the binary was built with support for modular feature "Track per-packet connection statistics". +// Specifically, it's whether the binary was NOT built with the "ts_omit_connstats" build tag. +// It's a const so it can be used for dead code elimination. +const HasConnStats = false diff --git a/feature/buildfeatures/feature_connstats_enabled.go b/feature/buildfeatures/feature_connstats_enabled.go new file mode 100644 index 000000000..c0451ce1e --- /dev/null +++ b/feature/buildfeatures/feature_connstats_enabled.go @@ -0,0 +1,13 @@ +// Copyright (c) Tailscale Inc & AUTHORS +// SPDX-License-Identifier: BSD-3-Clause + +// Code generated by gen.go; DO NOT EDIT. + +//go:build !ts_omit_connstats + +package buildfeatures + +// HasConnStats is whether the binary was built with support for modular feature "Track per-packet connection statistics". +// Specifically, it's whether the binary was NOT built with the "ts_omit_connstats" build tag. +// It's a const so it can be used for dead code elimination. +const HasConnStats = true diff --git a/feature/featuretags/featuretags.go b/feature/featuretags/featuretags.go index 5884d48d5..4ae4e1b86 100644 --- a/feature/featuretags/featuretags.go +++ b/feature/featuretags/featuretags.go @@ -130,7 +130,11 @@ var Features = map[FeatureTag]FeatureMeta{ Deps: []FeatureTag{"c2n"}, }, "completion": {Sym: "Completion", Desc: "CLI shell completion"}, - "cloud": {Sym: "Cloud", Desc: "detect cloud environment to learn instances IPs and DNS servers"}, + "connstats": { + Sym: "ConnStats", + Desc: "Track per-packet connection statistics", + }, + "cloud": {Sym: "Cloud", Desc: "detect cloud environment to learn instances IPs and DNS servers"}, "dbus": { Sym: "DBus", Desc: "Linux DBus support", diff --git a/net/connstats/stats.go b/net/connstats/stats.go index 4e6d8e109..44b276254 100644 --- a/net/connstats/stats.go +++ b/net/connstats/stats.go @@ -1,6 +1,8 @@ // Copyright (c) Tailscale Inc & AUTHORS // SPDX-License-Identifier: BSD-3-Clause +//go:build !ts_omit_connstats + // Package connstats maintains statistics about connections // flowing through a TUN device (which operate at the IP layer). package connstats diff --git a/net/connstats/stats_omit.go b/net/connstats/stats_omit.go new file mode 100644 index 000000000..15d16c9e4 --- /dev/null +++ b/net/connstats/stats_omit.go @@ -0,0 +1,24 @@ +// Copyright (c) Tailscale Inc & AUTHORS +// SPDX-License-Identifier: BSD-3-Clause + +//go:build ts_omit_connstats + +package connstats + +import ( + "context" + "net/netip" + "time" +) + +type Statistics struct{} + +func NewStatistics(maxPeriod time.Duration, maxConns int, dump func(start, end time.Time, virtual, physical any)) *Statistics { + return &Statistics{} +} + +func (s *Statistics) UpdateTxVirtual(b []byte) {} +func (s *Statistics) UpdateRxVirtual(b []byte) {} +func (s *Statistics) UpdateTxPhysical(src netip.Addr, dst netip.AddrPort, packets, bytes int) {} +func (s *Statistics) UpdateRxPhysical(src netip.Addr, dst netip.AddrPort, packets, bytes int) {} +func (s *Statistics) Shutdown(context.Context) error { return nil } diff --git a/net/tstun/wrap.go b/net/tstun/wrap.go index c94844c90..a6d88075d 100644 --- a/net/tstun/wrap.go +++ b/net/tstun/wrap.go @@ -973,8 +973,10 @@ func (t *Wrapper) Read(buffs [][]byte, sizes []int, offset int) (int, error) { panic(fmt.Sprintf("short copy: %d != %d", n, len(data)-res.dataOffset)) } sizes[buffsPos] = n - if stats := t.stats.Load(); stats != nil { - stats.UpdateTxVirtual(p.Buffer()) + if buildfeatures.HasConnStats { + if stats := t.stats.Load(); stats != nil { + stats.UpdateTxVirtual(p.Buffer()) + } } buffsPos++ } @@ -1098,9 +1100,11 @@ func (t *Wrapper) injectedRead(res tunInjectedRead, outBuffs [][]byte, sizes []i n, err = tun.GSOSplit(pkt, gsoOptions, outBuffs, sizes, offset) } - if stats := t.stats.Load(); stats != nil { - for i := 0; i < n; i++ { - stats.UpdateTxVirtual(outBuffs[i][offset : offset+sizes[i]]) + if buildfeatures.HasConnStats { + if stats := t.stats.Load(); stats != nil { + for i := 0; i < n; i++ { + stats.UpdateTxVirtual(outBuffs[i][offset : offset+sizes[i]]) + } } } @@ -1266,9 +1270,11 @@ func (t *Wrapper) Write(buffs [][]byte, offset int) (int, error) { } func (t *Wrapper) tdevWrite(buffs [][]byte, offset int) (int, error) { - if stats := t.stats.Load(); stats != nil { - for i := range buffs { - stats.UpdateRxVirtual((buffs)[i][offset:]) + if buildfeatures.HasConnStats { + if stats := t.stats.Load(); stats != nil { + for i := range buffs { + stats.UpdateRxVirtual((buffs)[i][offset:]) + } } } return t.tdev.Write(buffs, offset) @@ -1490,7 +1496,9 @@ func (t *Wrapper) Unwrap() tun.Device { // SetStatistics specifies a per-connection statistics aggregator. // Nil may be specified to disable statistics gathering. func (t *Wrapper) SetStatistics(stats *connstats.Statistics) { - t.stats.Store(stats) + if buildfeatures.HasConnStats { + t.stats.Store(stats) + } } var ( diff --git a/wgengine/magicsock/magicsock.go b/wgengine/magicsock/magicsock.go index b6cb7b336..76fbfb3b4 100644 --- a/wgengine/magicsock/magicsock.go +++ b/wgengine/magicsock/magicsock.go @@ -1865,8 +1865,10 @@ func (c *Conn) receiveIP(b []byte, ipp netip.AddrPort, cache *epAddrEndpointCach now := mono.Now() ep.lastRecvUDPAny.StoreAtomic(now) connNoted := ep.noteRecvActivity(src, now) - if stats := c.stats.Load(); stats != nil { - stats.UpdateRxPhysical(ep.nodeAddr, ipp, 1, geneveInclusivePacketLen) + if buildfeatures.HasConnStats { + if stats := c.stats.Load(); stats != nil { + stats.UpdateRxPhysical(ep.nodeAddr, ipp, 1, geneveInclusivePacketLen) + } } if src.vni.IsSet() && (connNoted || looksLikeInitiationMsg(b)) { // connNoted is periodic, but we also want to verify if the peer is who @@ -3743,7 +3745,9 @@ func (c *Conn) UpdateStatus(sb *ipnstate.StatusBuilder) { // SetStatistics specifies a per-connection statistics aggregator. // Nil may be specified to disable statistics gathering. func (c *Conn) SetStatistics(stats *connstats.Statistics) { - c.stats.Store(stats) + if buildfeatures.HasConnStats { + c.stats.Store(stats) + } } // SetHomeless sets whether magicsock should idle harder and not have a DERP diff --git a/wgengine/netlog/netlog.go b/wgengine/netlog/netlog.go index b7281e542..7e1938d27 100644 --- a/wgengine/netlog/netlog.go +++ b/wgengine/netlog/netlog.go @@ -19,6 +19,7 @@ import ( "sync" "time" + "tailscale.com/feature/buildfeatures" "tailscale.com/health" "tailscale.com/logpolicy" "tailscale.com/logtail" @@ -130,20 +131,24 @@ func (nl *Logger) Startup(nodeID tailcfg.StableNodeID, nodeLogID, domainLogID lo // can upload to the Tailscale log service, so stay below this limit. const maxLogSize = 256 << 10 const maxConns = (maxLogSize - netlogtype.MaxMessageJSONSize) / netlogtype.MaxConnectionCountsJSONSize - nl.stats = connstats.NewStatistics(pollPeriod, maxConns, func(start, end time.Time, virtual, physical map[netlogtype.Connection]netlogtype.Counts) { - nl.mu.Lock() - addrs := nl.addrs - prefixes := nl.prefixes - nl.mu.Unlock() - recordStatistics(nl.logger, nodeID, start, end, virtual, physical, addrs, prefixes, logExitFlowEnabledEnabled) - }) + if buildfeatures.HasConnStats { + nl.stats = connstats.NewStatistics(pollPeriod, maxConns, func(start, end time.Time, virtual, physical map[netlogtype.Connection]netlogtype.Counts) { + nl.mu.Lock() + addrs := nl.addrs + prefixes := nl.prefixes + nl.mu.Unlock() + recordStatistics(nl.logger, nodeID, start, end, virtual, physical, addrs, prefixes, logExitFlowEnabledEnabled) + }) + } // Register the connection tracker into the TUN device. if tun == nil { tun = noopDevice{} } nl.tun = tun - nl.tun.SetStatistics(nl.stats) + if buildfeatures.HasConnStats { + nl.tun.SetStatistics(nl.stats) + } // Register the connection tracker into magicsock. if sock == nil {