mirror of
https://github.com/tailscale/tailscale.git
synced 2026-06-27 01:05:54 -04:00
wgengine/netlog: stop using netmap.NetworkMap type, use LocalBackend
The Logger previously took a *netmap.NetworkMap at Startup and on every ReconfigNetworkMap call, denormalizing it into per-IP and self lookup maps. That denormalization is O(n) over all peers and ran on every netmap update, contributing to the broader quadratic behavior we want to eliminate when a single peer is added or removed. Instead, this makes netlog ask LocalBackend (well, nodeBackend) for the info it needs, letting us remove the netmap.NetworkMap type entirely from the netlog package. This is a dependency to removing the netmap.NetworkMap type from upstream callers, like wgengine.Engine in general. Updates #12542 Change-Id: Ib5f2de96e788a667332c0a6f7ac833b3d0053b5c Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
committed by
Brad Fitzpatrick
parent
994b2c8459
commit
8f210454dd
@@ -919,7 +919,7 @@ tailscale.com/cmd/k8s-operator dependencies: (generated by github.com/tailscale/
|
|||||||
tailscale.com/wgengine/filter from tailscale.com/control/controlclient+
|
tailscale.com/wgengine/filter from tailscale.com/control/controlclient+
|
||||||
tailscale.com/wgengine/filter/filtertype from tailscale.com/types/netmap+
|
tailscale.com/wgengine/filter/filtertype from tailscale.com/types/netmap+
|
||||||
💣 tailscale.com/wgengine/magicsock from tailscale.com/ipn/ipnlocal+
|
💣 tailscale.com/wgengine/magicsock from tailscale.com/ipn/ipnlocal+
|
||||||
tailscale.com/wgengine/netlog from tailscale.com/wgengine
|
tailscale.com/wgengine/netlog from tailscale.com/wgengine+
|
||||||
tailscale.com/wgengine/netstack from tailscale.com/tsnet
|
tailscale.com/wgengine/netstack from tailscale.com/tsnet
|
||||||
tailscale.com/wgengine/netstack/gro from tailscale.com/net/tstun+
|
tailscale.com/wgengine/netstack/gro from tailscale.com/net/tstun+
|
||||||
tailscale.com/wgengine/router from tailscale.com/ipn/ipnlocal+
|
tailscale.com/wgengine/router from tailscale.com/ipn/ipnlocal+
|
||||||
|
|||||||
@@ -194,7 +194,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
|
|||||||
tailscale.com/wgengine/filter from tailscale.com/control/controlclient+
|
tailscale.com/wgengine/filter from tailscale.com/control/controlclient+
|
||||||
tailscale.com/wgengine/filter/filtertype from tailscale.com/types/netmap+
|
tailscale.com/wgengine/filter/filtertype from tailscale.com/types/netmap+
|
||||||
💣 tailscale.com/wgengine/magicsock from tailscale.com/ipn/ipnlocal+
|
💣 tailscale.com/wgengine/magicsock from tailscale.com/ipn/ipnlocal+
|
||||||
tailscale.com/wgengine/netlog from tailscale.com/wgengine
|
tailscale.com/wgengine/netlog from tailscale.com/wgengine+
|
||||||
tailscale.com/wgengine/netstack/gro from tailscale.com/net/tstun+
|
tailscale.com/wgengine/netstack/gro from tailscale.com/net/tstun+
|
||||||
tailscale.com/wgengine/router from tailscale.com/cmd/tailscaled+
|
tailscale.com/wgengine/router from tailscale.com/cmd/tailscaled+
|
||||||
tailscale.com/wgengine/wgcfg from tailscale.com/ipn/ipnlocal+
|
tailscale.com/wgengine/wgcfg from tailscale.com/ipn/ipnlocal+
|
||||||
|
|||||||
@@ -215,7 +215,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
|
|||||||
tailscale.com/wgengine/filter from tailscale.com/control/controlclient+
|
tailscale.com/wgengine/filter from tailscale.com/control/controlclient+
|
||||||
tailscale.com/wgengine/filter/filtertype from tailscale.com/types/netmap+
|
tailscale.com/wgengine/filter/filtertype from tailscale.com/types/netmap+
|
||||||
💣 tailscale.com/wgengine/magicsock from tailscale.com/ipn/ipnlocal+
|
💣 tailscale.com/wgengine/magicsock from tailscale.com/ipn/ipnlocal+
|
||||||
tailscale.com/wgengine/netlog from tailscale.com/wgengine
|
tailscale.com/wgengine/netlog from tailscale.com/wgengine+
|
||||||
tailscale.com/wgengine/netstack/gro from tailscale.com/net/tstun+
|
tailscale.com/wgengine/netstack/gro from tailscale.com/net/tstun+
|
||||||
tailscale.com/wgengine/router from tailscale.com/cmd/tailscaled+
|
tailscale.com/wgengine/router from tailscale.com/cmd/tailscaled+
|
||||||
tailscale.com/wgengine/wgcfg from tailscale.com/ipn/ipnlocal+
|
tailscale.com/wgengine/wgcfg from tailscale.com/ipn/ipnlocal+
|
||||||
|
|||||||
@@ -503,7 +503,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
|
|||||||
tailscale.com/wgengine/filter from tailscale.com/control/controlclient+
|
tailscale.com/wgengine/filter from tailscale.com/control/controlclient+
|
||||||
tailscale.com/wgengine/filter/filtertype from tailscale.com/types/netmap+
|
tailscale.com/wgengine/filter/filtertype from tailscale.com/types/netmap+
|
||||||
💣 tailscale.com/wgengine/magicsock from tailscale.com/ipn/ipnlocal+
|
💣 tailscale.com/wgengine/magicsock from tailscale.com/ipn/ipnlocal+
|
||||||
tailscale.com/wgengine/netlog from tailscale.com/wgengine
|
tailscale.com/wgengine/netlog from tailscale.com/wgengine+
|
||||||
tailscale.com/wgengine/netstack from tailscale.com/cmd/tailscaled
|
tailscale.com/wgengine/netstack from tailscale.com/cmd/tailscaled
|
||||||
tailscale.com/wgengine/netstack/gro from tailscale.com/net/tstun+
|
tailscale.com/wgengine/netstack/gro from tailscale.com/net/tstun+
|
||||||
tailscale.com/wgengine/router from tailscale.com/cmd/tailscaled+
|
tailscale.com/wgengine/router from tailscale.com/cmd/tailscaled+
|
||||||
|
|||||||
@@ -317,7 +317,7 @@ tailscale.com/cmd/tsidp dependencies: (generated by github.com/tailscale/depawar
|
|||||||
tailscale.com/wgengine/filter from tailscale.com/control/controlclient+
|
tailscale.com/wgengine/filter from tailscale.com/control/controlclient+
|
||||||
tailscale.com/wgengine/filter/filtertype from tailscale.com/types/netmap+
|
tailscale.com/wgengine/filter/filtertype from tailscale.com/types/netmap+
|
||||||
💣 tailscale.com/wgengine/magicsock from tailscale.com/ipn/ipnlocal+
|
💣 tailscale.com/wgengine/magicsock from tailscale.com/ipn/ipnlocal+
|
||||||
tailscale.com/wgengine/netlog from tailscale.com/wgengine
|
tailscale.com/wgengine/netlog from tailscale.com/wgengine+
|
||||||
tailscale.com/wgengine/netstack from tailscale.com/tsnet
|
tailscale.com/wgengine/netstack from tailscale.com/tsnet
|
||||||
tailscale.com/wgengine/netstack/gro from tailscale.com/net/tstun+
|
tailscale.com/wgengine/netstack/gro from tailscale.com/net/tstun+
|
||||||
tailscale.com/wgengine/router from tailscale.com/ipn/ipnlocal+
|
tailscale.com/wgengine/router from tailscale.com/ipn/ipnlocal+
|
||||||
|
|||||||
@@ -105,6 +105,7 @@
|
|||||||
"tailscale.com/wgengine"
|
"tailscale.com/wgengine"
|
||||||
"tailscale.com/wgengine/filter"
|
"tailscale.com/wgengine/filter"
|
||||||
"tailscale.com/wgengine/magicsock"
|
"tailscale.com/wgengine/magicsock"
|
||||||
|
"tailscale.com/wgengine/netlog"
|
||||||
"tailscale.com/wgengine/router"
|
"tailscale.com/wgengine/router"
|
||||||
"tailscale.com/wgengine/wgcfg"
|
"tailscale.com/wgengine/wgcfg"
|
||||||
"tailscale.com/wgengine/wgcfg/nmcfg"
|
"tailscale.com/wgengine/wgcfg/nmcfg"
|
||||||
@@ -645,6 +646,7 @@ func NewLocalBackend(logf logger.Logf, logID logid.PublicID, sys *tsd.System, lo
|
|||||||
|
|
||||||
e.SetPeerByIPPacketFunc(b.lookupPeerByIP)
|
e.SetPeerByIPPacketFunc(b.lookupPeerByIP)
|
||||||
e.SetPeerSessionStateFunc(b.onPeerWireGuardState)
|
e.SetPeerSessionStateFunc(b.onPeerWireGuardState)
|
||||||
|
e.SetNetLogNodeSource(netLogNodeSource{b})
|
||||||
|
|
||||||
if sys.InitialConfig != nil {
|
if sys.InitialConfig != nil {
|
||||||
if err := b.initPrefsFromConfig(sys.InitialConfig); err != nil {
|
if err := b.initPrefsFromConfig(sys.InitialConfig); err != nil {
|
||||||
@@ -8046,6 +8048,40 @@ func (n noiseRoundTripper) RoundTrip(req *http.Request) (*http.Response, error)
|
|||||||
return n.lb.DoNoiseRequest(req)
|
return n.lb.DoNoiseRequest(req)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// netLogNodeSource adapts LocalBackend's nodeBackend to [netlog.NodeSource].
|
||||||
|
// Each method consults [LocalBackend.currentNode] so that profile rotations
|
||||||
|
// are picked up automatically without re-installing the source.
|
||||||
|
type netLogNodeSource struct {
|
||||||
|
b *LocalBackend
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s netLogNodeSource) SelfNode() (tailcfg.NodeView, tailcfg.UserProfileView) {
|
||||||
|
nb := s.b.currentNode()
|
||||||
|
self := nb.Self()
|
||||||
|
if !self.Valid() {
|
||||||
|
return tailcfg.NodeView{}, tailcfg.UserProfileView{}
|
||||||
|
}
|
||||||
|
up, _ := nb.UserByID(self.User())
|
||||||
|
return self, up
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s netLogNodeSource) NodeByAddr(addr netip.Addr) (_ tailcfg.NodeView, _ tailcfg.UserProfileView, ok bool) {
|
||||||
|
nb := s.b.currentNode()
|
||||||
|
nid, ok := nb.NodeByAddr(addr)
|
||||||
|
if !ok {
|
||||||
|
return tailcfg.NodeView{}, tailcfg.UserProfileView{}, false
|
||||||
|
}
|
||||||
|
nv, ok := nb.NodeByID(nid)
|
||||||
|
if !ok {
|
||||||
|
return tailcfg.NodeView{}, tailcfg.UserProfileView{}, false
|
||||||
|
}
|
||||||
|
up, _ := nb.UserByID(nv.User())
|
||||||
|
return nv, up, true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compile-time assertion that netLogNodeSource implements [netlog.NodeSource].
|
||||||
|
var _ netlog.NodeSource = netLogNodeSource{}
|
||||||
|
|
||||||
// ActiveSSHConns returns the number of active SSH connections,
|
// ActiveSSHConns returns the number of active SSH connections,
|
||||||
// or 0 if SSH is not linked into the binary or available on the platform.
|
// or 0 if SSH is not linked into the binary or available on the platform.
|
||||||
func (b *LocalBackend) ActiveSSHConns() int {
|
func (b *LocalBackend) ActiveSSHConns() int {
|
||||||
|
|||||||
@@ -47,6 +47,7 @@
|
|||||||
"tailscale.com/wgengine"
|
"tailscale.com/wgengine"
|
||||||
"tailscale.com/wgengine/filter"
|
"tailscale.com/wgengine/filter"
|
||||||
"tailscale.com/wgengine/magicsock"
|
"tailscale.com/wgengine/magicsock"
|
||||||
|
"tailscale.com/wgengine/netlog"
|
||||||
"tailscale.com/wgengine/router"
|
"tailscale.com/wgengine/router"
|
||||||
"tailscale.com/wgengine/wgcfg"
|
"tailscale.com/wgengine/wgcfg"
|
||||||
"tailscale.com/wgengine/wgint"
|
"tailscale.com/wgengine/wgint"
|
||||||
@@ -1984,6 +1985,7 @@ func (e *mockEngine) InstallCaptureHook(packet.CaptureCallback) {}
|
|||||||
func (e *mockEngine) SetPeerByIPPacketFunc(func(netip.Addr) (_ key.NodePublic, ok bool)) {}
|
func (e *mockEngine) SetPeerByIPPacketFunc(func(netip.Addr) (_ key.NodePublic, ok bool)) {}
|
||||||
func (e *mockEngine) SetPeerSessionStateFunc(func(key.NodePublic, wgengine.PeerWireGuardState)) {
|
func (e *mockEngine) SetPeerSessionStateFunc(func(key.NodePublic, wgengine.PeerWireGuardState)) {
|
||||||
}
|
}
|
||||||
|
func (e *mockEngine) SetNetLogNodeSource(netlog.NodeSource) {}
|
||||||
|
|
||||||
func (e *mockEngine) Close() {
|
func (e *mockEngine) Close() {
|
||||||
e.mu.Lock()
|
e.mu.Lock()
|
||||||
|
|||||||
@@ -312,7 +312,7 @@ tailscale.com/tsnet dependencies: (generated by github.com/tailscale/depaware)
|
|||||||
tailscale.com/wgengine/filter from tailscale.com/control/controlclient+
|
tailscale.com/wgengine/filter from tailscale.com/control/controlclient+
|
||||||
tailscale.com/wgengine/filter/filtertype from tailscale.com/types/netmap+
|
tailscale.com/wgengine/filter/filtertype from tailscale.com/types/netmap+
|
||||||
💣 tailscale.com/wgengine/magicsock from tailscale.com/ipn/ipnlocal+
|
💣 tailscale.com/wgengine/magicsock from tailscale.com/ipn/ipnlocal+
|
||||||
tailscale.com/wgengine/netlog from tailscale.com/wgengine
|
tailscale.com/wgengine/netlog from tailscale.com/wgengine+
|
||||||
tailscale.com/wgengine/netstack from tailscale.com/tsnet
|
tailscale.com/wgengine/netstack from tailscale.com/tsnet
|
||||||
tailscale.com/wgengine/netstack/gro from tailscale.com/net/tstun+
|
tailscale.com/wgengine/netstack/gro from tailscale.com/net/tstun+
|
||||||
tailscale.com/wgengine/router from tailscale.com/ipn/ipnlocal+
|
tailscale.com/wgengine/router from tailscale.com/ipn/ipnlocal+
|
||||||
|
|||||||
@@ -24,12 +24,12 @@
|
|||||||
"tailscale.com/net/sockstats"
|
"tailscale.com/net/sockstats"
|
||||||
"tailscale.com/net/tsaddr"
|
"tailscale.com/net/tsaddr"
|
||||||
"tailscale.com/syncs"
|
"tailscale.com/syncs"
|
||||||
|
"tailscale.com/tailcfg"
|
||||||
"tailscale.com/types/ipproto"
|
"tailscale.com/types/ipproto"
|
||||||
"tailscale.com/types/logger"
|
"tailscale.com/types/logger"
|
||||||
"tailscale.com/types/logid"
|
"tailscale.com/types/logid"
|
||||||
"tailscale.com/types/netlogfunc"
|
"tailscale.com/types/netlogfunc"
|
||||||
"tailscale.com/types/netlogtype"
|
"tailscale.com/types/netlogtype"
|
||||||
"tailscale.com/types/netmap"
|
|
||||||
"tailscale.com/util/eventbus"
|
"tailscale.com/util/eventbus"
|
||||||
"tailscale.com/util/set"
|
"tailscale.com/util/set"
|
||||||
"tailscale.com/wgengine/router"
|
"tailscale.com/wgengine/router"
|
||||||
@@ -51,6 +51,19 @@ type noopDevice struct{
|
|||||||
|
|
||||||
func (noopDevice) SetConnectionCounter(netlogfunc.ConnectionCounter) {}
|
func (noopDevice) SetConnectionCounter(netlogfunc.ConnectionCounter) {}
|
||||||
|
|
||||||
|
// NodeSource provides node lookups for the network logger.
|
||||||
|
// Methods may be called concurrently.
|
||||||
|
type NodeSource interface {
|
||||||
|
// SelfNode returns the local node and its owning user profile.
|
||||||
|
// Both views may be invalid if no self node is known yet.
|
||||||
|
SelfNode() (node tailcfg.NodeView, user tailcfg.UserProfileView)
|
||||||
|
|
||||||
|
// NodeByAddr returns the node assigned the given address along with
|
||||||
|
// its owning user profile.
|
||||||
|
// ok is false if no node is known to own addr.
|
||||||
|
NodeByAddr(addr netip.Addr) (node tailcfg.NodeView, user tailcfg.UserProfileView, ok bool)
|
||||||
|
}
|
||||||
|
|
||||||
// Logger logs statistics about every connection.
|
// Logger logs statistics about every connection.
|
||||||
// At present, it only logs connections within a tailscale network.
|
// At present, it only logs connections within a tailscale network.
|
||||||
// By default, exit node traffic is not logged for privacy reasons
|
// By default, exit node traffic is not logged for privacy reasons
|
||||||
@@ -69,10 +82,8 @@ type Logger struct {
|
|||||||
recordsChan chan record // set to nil when shutdown
|
recordsChan chan record // set to nil when shutdown
|
||||||
flushTimer *time.Timer // fires when record should flush to recordsChan
|
flushTimer *time.Timer // fires when record should flush to recordsChan
|
||||||
|
|
||||||
// Information about Tailscale nodes.
|
// source provides node lookups. Set by Startup, cleared by shutdownLocked.
|
||||||
// These are read-only once updated by ReconfigNetworkMap.
|
source NodeSource
|
||||||
selfNode nodeUser
|
|
||||||
allNodes map[netip.Addr]nodeUser // includes selfNode; nodeUser values are always valid
|
|
||||||
|
|
||||||
// Information about routes.
|
// Information about routes.
|
||||||
// These are read-only once updated by ReconfigRoutes.
|
// These are read-only once updated by ReconfigRoutes.
|
||||||
@@ -115,14 +126,20 @@ func (nl *Logger) Running() bool {
|
|||||||
// The sock is used to populated the PhysicalTraffic field in [netlogtype.Message].
|
// The sock is used to populated the PhysicalTraffic field in [netlogtype.Message].
|
||||||
//
|
//
|
||||||
// The netMon parameter is optional; if non-nil it's used to do faster interface lookups.
|
// The netMon parameter is optional; if non-nil it's used to do faster interface lookups.
|
||||||
func (nl *Logger) Startup(logf logger.Logf, nm *netmap.NetworkMap, nodeLogID, domainLogID logid.PrivateID, tun, sock Device, netMon *netmon.Monitor, health *health.Tracker, bus *eventbus.Bus, logExitFlowEnabledEnabled bool) error {
|
//
|
||||||
|
// source provides on-demand node lookups for connections seen by the logger.
|
||||||
|
// It must be non-nil.
|
||||||
|
func (nl *Logger) Startup(logf logger.Logf, source NodeSource, nodeLogID, domainLogID logid.PrivateID, tun, sock Device, netMon *netmon.Monitor, health *health.Tracker, bus *eventbus.Bus, logExitFlowEnabledEnabled bool) error {
|
||||||
nl.mu.Lock()
|
nl.mu.Lock()
|
||||||
defer nl.mu.Unlock()
|
defer nl.mu.Unlock()
|
||||||
|
|
||||||
if nl.shutdownLocked != nil {
|
if nl.shutdownLocked != nil {
|
||||||
return fmt.Errorf("network logger already running")
|
return fmt.Errorf("network logger already running")
|
||||||
}
|
}
|
||||||
nl.selfNode, nl.allNodes = makeNodeMaps(nm)
|
if source == nil {
|
||||||
|
return fmt.Errorf("network logger requires a non-nil NodeSource")
|
||||||
|
}
|
||||||
|
nl.source = source
|
||||||
|
|
||||||
// Startup a log stream to Tailscale's logging service.
|
// Startup a log stream to Tailscale's logging service.
|
||||||
if logf == nil {
|
if logf == nil {
|
||||||
@@ -194,8 +211,7 @@ func (nl *Logger) Startup(logf logger.Logf, nm *netmap.NetworkMap, nodeLogID, do
|
|||||||
|
|
||||||
// Purge state.
|
// Purge state.
|
||||||
nl.shutdownLocked = nil
|
nl.shutdownLocked = nil
|
||||||
nl.selfNode = nodeUser{}
|
nl.source = nil
|
||||||
nl.allNodes = nil
|
|
||||||
nl.routeAddrs = nil
|
nl.routeAddrs = nil
|
||||||
nl.routePrefixes = nil
|
nl.routePrefixes = nil
|
||||||
|
|
||||||
@@ -249,15 +265,15 @@ func (nl *Logger) addNewVirtConnLocked(c netlogtype.Connection) connType {
|
|||||||
var srcNodeLen, dstNodeLen int
|
var srcNodeLen, dstNodeLen int
|
||||||
srcNode, srcSeen := nl.record.seenNodes[c.Src.Addr()]
|
srcNode, srcSeen := nl.record.seenNodes[c.Src.Addr()]
|
||||||
if !srcSeen {
|
if !srcSeen {
|
||||||
srcNode = nl.allNodes[c.Src.Addr()]
|
if node, user, ok := nl.source.NodeByAddr(c.Src.Addr()); ok {
|
||||||
if srcNode.Valid() {
|
srcNode = nodeUser{node, user}
|
||||||
srcNodeLen = srcNode.jsonLen()
|
srcNodeLen = srcNode.jsonLen()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dstNode, dstSeen := nl.record.seenNodes[c.Dst.Addr()]
|
dstNode, dstSeen := nl.record.seenNodes[c.Dst.Addr()]
|
||||||
if !dstSeen {
|
if !dstSeen {
|
||||||
dstNode = nl.allNodes[c.Dst.Addr()]
|
if node, user, ok := nl.source.NodeByAddr(c.Dst.Addr()); ok {
|
||||||
if dstNode.Valid() {
|
dstNode = nodeUser{node, user}
|
||||||
dstNodeLen = dstNode.jsonLen()
|
dstNodeLen = dstNode.jsonLen()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -279,12 +295,10 @@ func (nl *Logger) addNewVirtConnLocked(c netlogtype.Connection) connType {
|
|||||||
nl.recordLen += netlogtype.MaxConnectionCountsJSONSize + srcNodeLen + dstNodeLen
|
nl.recordLen += netlogtype.MaxConnectionCountsJSONSize + srcNodeLen + dstNodeLen
|
||||||
|
|
||||||
// Classify the traffic type.
|
// Classify the traffic type.
|
||||||
var srcIsSelfNode bool
|
// srcNode == self iff NodeByAddr resolved the source to the same node as
|
||||||
if nl.selfNode.Valid() {
|
// the current record's self.
|
||||||
srcIsSelfNode = nl.selfNode.Addresses().ContainsFunc(func(p netip.Prefix) bool {
|
srcIsSelfNode := srcNode.Valid() && nl.record.selfNode.Valid() &&
|
||||||
return c.Src.Addr() == p.Addr() && p.IsSingleIP()
|
srcNode.ID() == nl.record.selfNode.ID()
|
||||||
})
|
|
||||||
}
|
|
||||||
switch {
|
switch {
|
||||||
case srcIsSelfNode && dstNode.Valid():
|
case srcIsSelfNode && dstNode.Valid():
|
||||||
return virtualTraffic
|
return virtualTraffic
|
||||||
@@ -335,8 +349,8 @@ func (nl *Logger) addNewPhysConnLocked(c netlogtype.Connection) {
|
|||||||
var srcNodeLen int
|
var srcNodeLen int
|
||||||
srcNode, srcSeen := nl.record.seenNodes[c.Src.Addr()]
|
srcNode, srcSeen := nl.record.seenNodes[c.Src.Addr()]
|
||||||
if !srcSeen {
|
if !srcSeen {
|
||||||
srcNode = nl.allNodes[c.Src.Addr()]
|
if node, user, ok := nl.source.NodeByAddr(c.Src.Addr()); ok {
|
||||||
if srcNode.Valid() {
|
srcNode = nodeUser{node, user}
|
||||||
srcNodeLen = srcNode.jsonLen()
|
srcNodeLen = srcNode.jsonLen()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -361,14 +375,16 @@ func (nl *Logger) initRecordLocked() {
|
|||||||
if nl.recordLen != 0 {
|
if nl.recordLen != 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
node, user := nl.source.SelfNode()
|
||||||
|
self := nodeUser{node, user}
|
||||||
nl.record = record{
|
nl.record = record{
|
||||||
selfNode: nl.selfNode,
|
selfNode: self,
|
||||||
start: time.Now().UTC(),
|
start: time.Now().UTC(),
|
||||||
seenNodes: make(map[netip.Addr]nodeUser),
|
seenNodes: make(map[netip.Addr]nodeUser),
|
||||||
virtConns: make(map[netlogtype.Connection]countsType),
|
virtConns: make(map[netlogtype.Connection]countsType),
|
||||||
physConns: make(map[netlogtype.Connection]netlogtype.Counts),
|
physConns: make(map[netlogtype.Connection]netlogtype.Counts),
|
||||||
}
|
}
|
||||||
nl.recordLen = netlogtype.MinMessageJSONSize + nl.selfNode.jsonLen()
|
nl.recordLen = netlogtype.MinMessageJSONSize + self.jsonLen()
|
||||||
|
|
||||||
// Start a time to auto-flush the record.
|
// Start a time to auto-flush the record.
|
||||||
// Avoid tickers since continually waking up a goroutine
|
// Avoid tickers since continually waking up a goroutine
|
||||||
@@ -406,39 +422,6 @@ func (nl *Logger) flushRecordLocked() {
|
|||||||
nl.recordLen = 0
|
nl.recordLen = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeNodeMaps(nm *netmap.NetworkMap) (selfNode nodeUser, allNodes map[netip.Addr]nodeUser) {
|
|
||||||
if nm == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
allNodes = make(map[netip.Addr]nodeUser)
|
|
||||||
if nm.SelfNode.Valid() {
|
|
||||||
selfNode = nodeUser{nm.SelfNode, nm.UserProfiles[nm.SelfNode.User()]}
|
|
||||||
for _, addr := range nm.SelfNode.Addresses().All() {
|
|
||||||
if addr.IsSingleIP() {
|
|
||||||
allNodes[addr.Addr()] = selfNode
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, peer := range nm.Peers {
|
|
||||||
if peer.Valid() {
|
|
||||||
for _, addr := range peer.Addresses().All() {
|
|
||||||
if addr.IsSingleIP() {
|
|
||||||
allNodes[addr.Addr()] = nodeUser{peer, nm.UserProfiles[peer.User()]}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return selfNode, allNodes
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReconfigNetworkMap configures the network logger with an updated netmap.
|
|
||||||
func (nl *Logger) ReconfigNetworkMap(nm *netmap.NetworkMap) {
|
|
||||||
selfNode, allNodes := makeNodeMaps(nm) // avoid holding lock while making maps
|
|
||||||
nl.mu.Lock()
|
|
||||||
nl.selfNode, nl.allNodes = selfNode, allNodes
|
|
||||||
nl.mu.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
func makeRouteMaps(cfg *router.Config) (addrs set.Set[netip.Addr], prefixes []netip.Prefix) {
|
func makeRouteMaps(cfg *router.Config) (addrs set.Set[netip.Addr], prefixes []netip.Prefix) {
|
||||||
addrs = make(set.Set[netip.Addr])
|
addrs = make(set.Set[netip.Addr])
|
||||||
insertPrefixes := func(rs []netip.Prefix) {
|
insertPrefixes := func(rs []netip.Prefix) {
|
||||||
|
|||||||
@@ -7,8 +7,11 @@
|
|||||||
|
|
||||||
type Logger struct{}
|
type Logger struct{}
|
||||||
|
|
||||||
func (*Logger) Startup(...any) error { return nil }
|
// NodeSource is a stub kept so the omit build does not break consumers that
|
||||||
func (*Logger) Running() bool { return false }
|
// reference the type. It has no methods.
|
||||||
func (*Logger) Shutdown(any) error { return nil }
|
type NodeSource any
|
||||||
func (*Logger) ReconfigNetworkMap(any) {}
|
|
||||||
func (*Logger) ReconfigRoutes(any) {}
|
func (*Logger) Startup(...any) error { return nil }
|
||||||
|
func (*Logger) Running() bool { return false }
|
||||||
|
func (*Logger) Shutdown(any) error { return nil }
|
||||||
|
func (*Logger) ReconfigRoutes(any) {}
|
||||||
|
|||||||
@@ -21,22 +21,74 @@
|
|||||||
"tailscale.com/types/bools"
|
"tailscale.com/types/bools"
|
||||||
"tailscale.com/types/ipproto"
|
"tailscale.com/types/ipproto"
|
||||||
"tailscale.com/types/netlogtype"
|
"tailscale.com/types/netlogtype"
|
||||||
"tailscale.com/types/netmap"
|
|
||||||
"tailscale.com/wgengine/router"
|
"tailscale.com/wgengine/router"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// nodeAndUser pairs a [tailcfg.NodeView] with its owning user profile.
|
||||||
|
type nodeAndUser struct {
|
||||||
|
node tailcfg.NodeView
|
||||||
|
user tailcfg.UserProfileView
|
||||||
|
}
|
||||||
|
|
||||||
|
// fakeNodeSource is a [NodeSource] implementation backed by static maps,
|
||||||
|
// for tests.
|
||||||
|
type fakeNodeSource struct {
|
||||||
|
self tailcfg.NodeView
|
||||||
|
selfUser tailcfg.UserProfileView
|
||||||
|
byAddr map[netip.Addr]nodeAndUser
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *fakeNodeSource) SelfNode() (tailcfg.NodeView, tailcfg.UserProfileView) {
|
||||||
|
return s.self, s.selfUser
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *fakeNodeSource) NodeByAddr(a netip.Addr) (_ tailcfg.NodeView, _ tailcfg.UserProfileView, ok bool) {
|
||||||
|
nu, ok := s.byAddr[a]
|
||||||
|
return nu.node, nu.user, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// newFakeNodeSource builds a [fakeNodeSource] from a self [tailcfg.NodeView],
|
||||||
|
// a list of peer [tailcfg.NodeView] values, and a UserProfile map.
|
||||||
|
// All single-IP addresses on self and the peers are indexed for NodeByAddr.
|
||||||
|
func newFakeNodeSource(self tailcfg.NodeView, peers []tailcfg.NodeView, users map[tailcfg.UserID]tailcfg.UserProfileView) *fakeNodeSource {
|
||||||
|
s := &fakeNodeSource{
|
||||||
|
byAddr: map[netip.Addr]nodeAndUser{},
|
||||||
|
}
|
||||||
|
if self.Valid() {
|
||||||
|
s.self = self
|
||||||
|
s.selfUser = users[self.User()]
|
||||||
|
for _, p := range self.Addresses().All() {
|
||||||
|
if p.IsSingleIP() {
|
||||||
|
s.byAddr[p.Addr()] = nodeAndUser{self, s.selfUser}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, peer := range peers {
|
||||||
|
if !peer.Valid() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
pu := users[peer.User()]
|
||||||
|
for _, p := range peer.Addresses().All() {
|
||||||
|
if p.IsSingleIP() {
|
||||||
|
s.byAddr[p.Addr()] = nodeAndUser{peer, pu}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
func TestEmbedNodeInfo(t *testing.T) {
|
func TestEmbedNodeInfo(t *testing.T) {
|
||||||
// Initialize the logger with a particular view of the netmap.
|
// Initialize the logger with a particular view of the node state.
|
||||||
var logger Logger
|
var logger Logger
|
||||||
logger.ReconfigNetworkMap(&netmap.NetworkMap{
|
logger.source = newFakeNodeSource(
|
||||||
SelfNode: (&tailcfg.Node{
|
(&tailcfg.Node{
|
||||||
StableID: "n123456CNTL",
|
StableID: "n123456CNTL",
|
||||||
ID: 123456,
|
ID: 123456,
|
||||||
Name: "test.tail123456.ts.net",
|
Name: "test.tail123456.ts.net",
|
||||||
Addresses: []netip.Prefix{prefix("100.1.2.3")},
|
Addresses: []netip.Prefix{prefix("100.1.2.3")},
|
||||||
Tags: []string{"tag:foo", "tag:bar"},
|
Tags: []string{"tag:foo", "tag:bar"},
|
||||||
}).View(),
|
}).View(),
|
||||||
Peers: []tailcfg.NodeView{
|
[]tailcfg.NodeView{
|
||||||
(&tailcfg.Node{
|
(&tailcfg.Node{
|
||||||
StableID: "n123457CNTL",
|
StableID: "n123457CNTL",
|
||||||
ID: 123457,
|
ID: 123457,
|
||||||
@@ -52,10 +104,10 @@ func TestEmbedNodeInfo(t *testing.T) {
|
|||||||
User: 54321,
|
User: 54321,
|
||||||
}).View(),
|
}).View(),
|
||||||
},
|
},
|
||||||
UserProfiles: map[tailcfg.UserID]tailcfg.UserProfileView{
|
map[tailcfg.UserID]tailcfg.UserProfileView{
|
||||||
54321: (&tailcfg.UserProfile{ID: 54321, LoginName: "peer@example.com"}).View(),
|
54321: (&tailcfg.UserProfile{ID: 54321, LoginName: "peer@example.com"}).View(),
|
||||||
},
|
},
|
||||||
})
|
)
|
||||||
logger.ReconfigRoutes(&router.Config{
|
logger.ReconfigRoutes(&router.Config{
|
||||||
SubnetRoutes: []netip.Prefix{
|
SubnetRoutes: []netip.Prefix{
|
||||||
prefix("172.16.1.1/16"),
|
prefix("172.16.1.1/16"),
|
||||||
@@ -148,6 +200,9 @@ func TestEmbedNodeInfo(t *testing.T) {
|
|||||||
|
|
||||||
func TestUpdateRace(t *testing.T) {
|
func TestUpdateRace(t *testing.T) {
|
||||||
var logger Logger
|
var logger Logger
|
||||||
|
// Install an empty fake source so NodeByAddr / SelfNode are exercised on
|
||||||
|
// the lookup path without returning any matches.
|
||||||
|
logger.source = &fakeNodeSource{}
|
||||||
logger.recordsChan = make(chan record, 1)
|
logger.recordsChan = make(chan record, 1)
|
||||||
go func(recordsChan chan record) {
|
go func(recordsChan chan record) {
|
||||||
for range recordsChan {
|
for range recordsChan {
|
||||||
@@ -167,11 +222,6 @@ func TestUpdateRace(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
group.Go(func() {
|
|
||||||
for range 1000 {
|
|
||||||
logger.ReconfigNetworkMap(new(netmap.NetworkMap))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
group.Go(func() {
|
group.Go(func() {
|
||||||
for range 1000 {
|
for range 1000 {
|
||||||
logger.ReconfigRoutes(new(router.Config))
|
logger.ReconfigRoutes(new(router.Config))
|
||||||
@@ -194,6 +244,7 @@ func randAddrPort() netip.AddrPort {
|
|||||||
|
|
||||||
func TestAutoFlushMaxConns(t *testing.T) {
|
func TestAutoFlushMaxConns(t *testing.T) {
|
||||||
var logger Logger
|
var logger Logger
|
||||||
|
logger.source = &fakeNodeSource{}
|
||||||
logger.recordsChan = make(chan record, 1)
|
logger.recordsChan = make(chan record, 1)
|
||||||
for i := 0; len(logger.recordsChan) == 0; i++ {
|
for i := 0; len(logger.recordsChan) == 0; i++ {
|
||||||
logger.updateVirtConn(0, netip.AddrPortFrom(netip.Addr{}, uint16(i)), netip.AddrPort{}, 1, 1, false)
|
logger.updateVirtConn(0, netip.AddrPortFrom(netip.Addr{}, uint16(i)), netip.AddrPort{}, 1, 1, false)
|
||||||
@@ -206,6 +257,7 @@ func TestAutoFlushMaxConns(t *testing.T) {
|
|||||||
|
|
||||||
func TestAutoFlushTimeout(t *testing.T) {
|
func TestAutoFlushTimeout(t *testing.T) {
|
||||||
var logger Logger
|
var logger Logger
|
||||||
|
logger.source = &fakeNodeSource{}
|
||||||
logger.recordsChan = make(chan record, 1)
|
logger.recordsChan = make(chan record, 1)
|
||||||
synctest.Test(t, func(t *testing.T) {
|
synctest.Test(t, func(t *testing.T) {
|
||||||
logger.updateVirtConn(0, netip.AddrPort{}, netip.AddrPort{}, 1, 1, false)
|
logger.updateVirtConn(0, netip.AddrPort{}, netip.AddrPort{}, 1, 1, false)
|
||||||
@@ -222,6 +274,7 @@ func TestAutoFlushTimeout(t *testing.T) {
|
|||||||
|
|
||||||
func BenchmarkUpdateSameConn(b *testing.B) {
|
func BenchmarkUpdateSameConn(b *testing.B) {
|
||||||
var logger Logger
|
var logger Logger
|
||||||
|
logger.source = &fakeNodeSource{}
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
for range b.N {
|
for range b.N {
|
||||||
logger.updateVirtConn(0, netip.AddrPort{}, netip.AddrPort{}, 1, 1, false)
|
logger.updateVirtConn(0, netip.AddrPort{}, netip.AddrPort{}, 1, 1, false)
|
||||||
@@ -230,6 +283,7 @@ func BenchmarkUpdateSameConn(b *testing.B) {
|
|||||||
|
|
||||||
func BenchmarkUpdateNewConns(b *testing.B) {
|
func BenchmarkUpdateNewConns(b *testing.B) {
|
||||||
var logger Logger
|
var logger Logger
|
||||||
|
logger.source = &fakeNodeSource{}
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
for i := range b.N {
|
for i := range b.N {
|
||||||
logger.updateVirtConn(0, netip.AddrPortFrom(netip.Addr{}, uint16(i)), netip.AddrPort{}, 1, 1, false)
|
logger.updateVirtConn(0, netip.AddrPortFrom(netip.Addr{}, uint16(i)), netip.AddrPort{}, 1, 1, false)
|
||||||
|
|||||||
@@ -157,6 +157,11 @@ type userspaceEngine struct {
|
|||||||
// networkLogger logs statistics about network connections.
|
// networkLogger logs statistics about network connections.
|
||||||
networkLogger netlog.Logger
|
networkLogger netlog.Logger
|
||||||
|
|
||||||
|
// netLogSource is the [netlog.NodeSource] installed via
|
||||||
|
// [Engine.SetNetLogNodeSource]; it is read when starting up the network
|
||||||
|
// logger from inside Reconfig. It may be nil if no source was installed.
|
||||||
|
netLogSource syncs.AtomicValue[netlog.NodeSource]
|
||||||
|
|
||||||
// tsmpLearnedDisco tracks per node key if a peer disco key was learned via TSMP.
|
// tsmpLearnedDisco tracks per node key if a peer disco key was learned via TSMP.
|
||||||
// wgLock must be held when using this map.
|
// wgLock must be held when using this map.
|
||||||
tsmpLearnedDisco map[key.NodePublic]key.DiscoPublic
|
tsmpLearnedDisco map[key.NodePublic]key.DiscoPublic
|
||||||
@@ -735,6 +740,12 @@ func (e *userspaceEngine) SetPeerSessionStateFunc(fn func(key.NodePublic, PeerWi
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetNetLogNodeSource installs the [netlog.NodeSource] used by the engine's
|
||||||
|
// network logger.
|
||||||
|
func (e *userspaceEngine) SetNetLogNodeSource(src netlog.NodeSource) {
|
||||||
|
e.netLogSource.Store(src)
|
||||||
|
}
|
||||||
|
|
||||||
func peerWireGuardStateFromDevice(state device.PeerSessionState) PeerWireGuardState {
|
func peerWireGuardStateFromDevice(state device.PeerSessionState) PeerWireGuardState {
|
||||||
switch state {
|
switch state {
|
||||||
case device.PeerSessionNone:
|
case device.PeerSessionNone:
|
||||||
@@ -969,7 +980,10 @@ func (e *userspaceEngine) Reconfig(cfg *wgcfg.Config, routerCfg *router.Config,
|
|||||||
tid := cfg.NetworkLogging.DomainID
|
tid := cfg.NetworkLogging.DomainID
|
||||||
logExitFlowEnabled := cfg.NetworkLogging.LogExitFlowEnabled
|
logExitFlowEnabled := cfg.NetworkLogging.LogExitFlowEnabled
|
||||||
e.logf("wgengine: Reconfig: starting up network logger (node:%s tailnet:%s)", nid.Public(), tid.Public())
|
e.logf("wgengine: Reconfig: starting up network logger (node:%s tailnet:%s)", nid.Public(), tid.Public())
|
||||||
if err := e.networkLogger.Startup(e.logf, nm, nid, tid, e.tundev, e.magicConn, e.netMon, e.health, e.eventBus, logExitFlowEnabled); err != nil {
|
src := e.netLogSource.Load()
|
||||||
|
if src == nil {
|
||||||
|
e.logf("wgengine: Reconfig: no NodeSource installed; network logger not started")
|
||||||
|
} else if err := e.networkLogger.Startup(e.logf, src, nid, tid, e.tundev, e.magicConn, e.netMon, e.health, e.eventBus, logExitFlowEnabled); err != nil {
|
||||||
e.logf("wgengine: Reconfig: error starting up network logger: %v", err)
|
e.logf("wgengine: Reconfig: error starting up network logger: %v", err)
|
||||||
}
|
}
|
||||||
e.networkLogger.ReconfigRoutes(routerCfg)
|
e.networkLogger.ReconfigRoutes(routerCfg)
|
||||||
@@ -1309,9 +1323,6 @@ func (e *userspaceEngine) SetNetworkMap(nm *netmap.NetworkMap) {
|
|||||||
curUDP, curTCP)
|
curUDP, curTCP)
|
||||||
e.tundev.ApplyGROKnobs(e.controlKnobs)
|
e.tundev.ApplyGROKnobs(e.controlKnobs)
|
||||||
}
|
}
|
||||||
if e.networkLogger.Running() {
|
|
||||||
e.networkLogger.ReconfigNetworkMap(nm)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *userspaceEngine) UpdateStatus(sb *ipnstate.StatusBuilder) {
|
func (e *userspaceEngine) UpdateStatus(sb *ipnstate.StatusBuilder) {
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
"tailscale.com/types/key"
|
"tailscale.com/types/key"
|
||||||
"tailscale.com/types/netmap"
|
"tailscale.com/types/netmap"
|
||||||
"tailscale.com/wgengine/filter"
|
"tailscale.com/wgengine/filter"
|
||||||
|
"tailscale.com/wgengine/netlog"
|
||||||
"tailscale.com/wgengine/router"
|
"tailscale.com/wgengine/router"
|
||||||
"tailscale.com/wgengine/wgcfg"
|
"tailscale.com/wgengine/wgcfg"
|
||||||
"tailscale.com/wgengine/wgint"
|
"tailscale.com/wgengine/wgint"
|
||||||
@@ -164,6 +165,13 @@ type Engine interface {
|
|||||||
// look up which peer should handle an outbound packet by destination IP.
|
// look up which peer should handle an outbound packet by destination IP.
|
||||||
SetPeerByIPPacketFunc(func(netip.Addr) (_ key.NodePublic, ok bool))
|
SetPeerByIPPacketFunc(func(netip.Addr) (_ key.NodePublic, ok bool))
|
||||||
|
|
||||||
|
// SetNetLogNodeSource installs the [netlog.NodeSource] used by the engine's
|
||||||
|
// network logger to look up Tailscale node info on demand.
|
||||||
|
//
|
||||||
|
// It is expected to be called once during LocalBackend construction,
|
||||||
|
// before any [Engine.Reconfig] call that starts up the network logger.
|
||||||
|
SetNetLogNodeSource(netlog.NodeSource)
|
||||||
|
|
||||||
// SetPeerSessionStateFunc installs a callback used to observe WireGuard
|
// SetPeerSessionStateFunc installs a callback used to observe WireGuard
|
||||||
// peer session state transitions.
|
// peer session state transitions.
|
||||||
//
|
//
|
||||||
|
|||||||
Reference in New Issue
Block a user