122 lines
4.0 KiB
Go
122 lines
4.0 KiB
Go
// Copyright (c) Tailscale Inc & contributors
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
// Package netlog registers the network flow logging feature and wires
|
|
// it into the WireGuard engine. The logger implementation itself lives
|
|
// in tailscale.com/wgengine/netlog.
|
|
package netlog
|
|
|
|
import (
|
|
"context"
|
|
"time"
|
|
|
|
"tailscale.com/envknob"
|
|
"tailscale.com/feature"
|
|
"tailscale.com/types/logid"
|
|
"tailscale.com/wgengine"
|
|
"tailscale.com/wgengine/netlog"
|
|
"tailscale.com/wgengine/router"
|
|
)
|
|
|
|
func init() {
|
|
if !feature.Register("netlog") {
|
|
return
|
|
}
|
|
wgengine.HookNewNetLogger.Set(newNetLogger)
|
|
}
|
|
|
|
// uploadTimeout is the maximum time to wait when shutting down the
|
|
// network logger as it uploads the final network log messages.
|
|
const uploadTimeout = 5 * time.Second
|
|
|
|
// netLogger implements [wgengine.NetLogger] on top of [netlog.Logger],
|
|
// tracking the logging identity and running state across engine
|
|
// reconfigurations. One netLogger exists per engine.
|
|
type netLogger struct {
|
|
deps wgengine.NetLoggerDeps
|
|
logger netlog.Logger
|
|
|
|
// The fields below are only accessed from Reconfig and
|
|
// ReconfigDone, which the engine serializes under its internal
|
|
// lock.
|
|
|
|
// nodeID and domainID are the logging IDs from the last Reconfig,
|
|
// or zero if logging was not enabled then.
|
|
nodeID, domainID logid.PrivateID
|
|
// logExitFlows is whether exit node flows were logged as of the
|
|
// last Reconfig.
|
|
logExitFlows bool
|
|
// shouldRun is whether the last Reconfig wanted the logger
|
|
// running. ReconfigDone consults it to shut down a stopping
|
|
// logger after the router has been reconfigured.
|
|
shouldRun bool
|
|
}
|
|
|
|
func newNetLogger(deps wgengine.NetLoggerDeps) wgengine.NetLogger {
|
|
return &netLogger{deps: deps}
|
|
}
|
|
|
|
func (nl *netLogger) Reconfig(routerCfg *router.Config, routerChanged bool) (changed bool) {
|
|
logf := nl.deps.Logf
|
|
src := nl.deps.Source()
|
|
|
|
var nodeID, domainID logid.PrivateID
|
|
var logExitFlows bool
|
|
if src != nil {
|
|
if nid, did, lef, ok := src.NetLogIDs(); ok {
|
|
nodeID, domainID, logExitFlows = nid, did, lef
|
|
}
|
|
}
|
|
idsValid := !nodeID.IsZero() && !domainID.IsZero()
|
|
idsWereValid := !nl.nodeID.IsZero() && !nl.domainID.IsZero()
|
|
changed = nodeID != nl.nodeID || domainID != nl.domainID || logExitFlows != nl.logExitFlows
|
|
idsChanged := idsValid && idsWereValid && changed
|
|
nl.nodeID, nl.domainID, nl.logExitFlows = nodeID, domainID, logExitFlows
|
|
|
|
nl.shouldRun = idsValid && !routerCfg.Equal(&router.Config{}) && !envknob.NoLogsNoSupport()
|
|
|
|
// Shut down the logger if the IDs changed while it was running,
|
|
// letting the startup logic below bring it back up with the new
|
|
// identity.
|
|
if idsChanged && nl.logger.Running() {
|
|
logf("wgengine: Reconfig: shutting down network logger")
|
|
nl.shutdown()
|
|
}
|
|
|
|
// Start the logger before the engine configures the router so
|
|
// that it captures initial packets.
|
|
if nl.shouldRun && !nl.logger.Running() {
|
|
logf("wgengine: Reconfig: starting up network logger (node:%s tailnet:%s)", nodeID.Public(), domainID.Public())
|
|
if err := nl.logger.Startup(logf, src, nodeID, domainID, nl.deps.Tun, nl.deps.Sock, nl.deps.NetMon, nl.deps.Health, nl.deps.Bus, logExitFlows); err != nil {
|
|
logf("wgengine: Reconfig: error starting up network logger: %v", err)
|
|
}
|
|
nl.logger.ReconfigRoutes(routerCfg)
|
|
} else if routerChanged && nl.logger.Running() {
|
|
nl.logger.ReconfigRoutes(routerCfg)
|
|
}
|
|
return changed
|
|
}
|
|
|
|
func (nl *netLogger) ReconfigDone() {
|
|
// Shut down a stopping logger only now, after the engine has
|
|
// configured the router, so that it captures final packets.
|
|
if !nl.shouldRun && nl.logger.Running() {
|
|
nl.deps.Logf("wgengine: Reconfig: shutting down network logger")
|
|
nl.shutdown()
|
|
}
|
|
}
|
|
|
|
func (nl *netLogger) Shutdown() {
|
|
nl.shutdown()
|
|
}
|
|
|
|
// shutdown stops the logger, waiting up to uploadTimeout for it to
|
|
// flush its final messages.
|
|
func (nl *netLogger) shutdown() {
|
|
ctx, cancel := context.WithTimeout(context.Background(), uploadTimeout)
|
|
defer cancel()
|
|
if err := nl.logger.Shutdown(ctx); err != nil {
|
|
nl.deps.Logf("wgengine: error shutting down network logger: %v", err)
|
|
}
|
|
}
|