This applies the same treatment from 8f210454dd (netlog) to wglog,
ending use of netmap.NetworkMap and instead getting the canonical data
from LocalBackend/nodeBackend.
This is a dependency to removing the netmap.NetworkMap from
upstream callers, like wgengine.Engine in general.
Updates #12542
Change-Id: Icb5af0799322def048a6f594b49f7d11273f025d
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
160 lines
5.2 KiB
Go
160 lines
5.2 KiB
Go
// Copyright (c) Tailscale Inc & contributors
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
// Package wglog contains logging helpers for wireguard-go.
|
|
package wglog
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
"sync"
|
|
|
|
"github.com/tailscale/wireguard-go/device"
|
|
"tailscale.com/envknob"
|
|
"tailscale.com/types/logger"
|
|
"tailscale.com/util/mak"
|
|
)
|
|
|
|
// A Logger is a wireguard-go log wrapper that cleans up and rewrites log lines.
|
|
// It rewrites wireguard-go peer references like "peer(XXXX…YYYY)" into the
|
|
// Tailscale-conventional short string form like "[XXXXX]".
|
|
type Logger struct {
|
|
DeviceLogger *device.Logger
|
|
|
|
// lookup, when non-nil, returns the Tailscale-conventional short
|
|
// string for a wireguard-go-formatted peer string (e.g. it maps
|
|
// "peer(IMTB…r7lM)" to "[IMTBr]"), or "", false if no peer matches.
|
|
// It is set once at construction and not changed afterwards.
|
|
lookup func(wgString string) (tsString string, ok bool)
|
|
|
|
mu sync.Mutex // protects cache
|
|
|
|
// cache memoizes lookup results. The key is the wireguard-go
|
|
// peer-string form ("peer(XXXX…YYYY)") and the value is the
|
|
// Tailscale-conventional short-string form ("[XXXXX]").
|
|
// It is cleared in bulk by Invalidate when the underlying peer set
|
|
// may have changed.
|
|
cache map[string]string
|
|
}
|
|
|
|
// NewLogger creates a new logger for use with wireguard-go.
|
|
// This logger silences repetitive/unhelpful noisy log lines
|
|
// and rewrites peer keys from wireguard-go into Tailscale format.
|
|
//
|
|
// lookup, if non-nil, is consulted on cache misses to translate
|
|
// wireguard-go peer references in log lines. If lookup is nil,
|
|
// peer references are passed through unchanged.
|
|
func NewLogger(logf logger.Logf, lookup func(wgString string) (tsString string, ok bool)) *Logger {
|
|
const prefix = "wg: "
|
|
ret := &Logger{lookup: lookup}
|
|
wrapper := func(format string, args ...any) {
|
|
if strings.Contains(format, "Routine:") && !strings.Contains(format, "receive incoming") {
|
|
// wireguard-go logs as it starts and stops routines.
|
|
// Drop those; there are a lot of them, and they're just noise.
|
|
return
|
|
}
|
|
if strings.Contains(format, "Failed to send data packet") {
|
|
// Drop. See https://github.com/tailscale/tailscale/issues/1239.
|
|
return
|
|
}
|
|
if strings.Contains(format, "Interface up requested") || strings.Contains(format, "Interface down requested") {
|
|
// Drop. Logs 1/s constantly while the tun device is open.
|
|
// See https://github.com/tailscale/tailscale/issues/1388.
|
|
return
|
|
}
|
|
if strings.Contains(format, "Adding allowedip") {
|
|
// Drop. See https://github.com/tailscale/corp/issues/17532.
|
|
// AppConnectors (as one example) may have many subnet routes, and
|
|
// the messaging related to these is not specific enough to be
|
|
// useful.
|
|
return
|
|
}
|
|
if ret.lookup == nil {
|
|
// No lookup; log as originally planned.
|
|
logf(format, args...)
|
|
return
|
|
}
|
|
// Replace any *device.Peer-shaped fmt.Stringer args with the
|
|
// Tailscale-formatted version of themselves. Using *device.Peer
|
|
// directly makes this hard to test, so we string any fmt.Stringers,
|
|
// and if the string ends up matching a known peer, we substitute.
|
|
// This is slightly imprecise, in that we don't check the formatting
|
|
// verb. Oh well.
|
|
var newargs []any
|
|
for i, arg := range args {
|
|
s, ok := arg.(fmt.Stringer)
|
|
if !ok {
|
|
continue
|
|
}
|
|
tsStr, ok := ret.peerStringFor(s.String())
|
|
if !ok {
|
|
continue
|
|
}
|
|
if newargs == nil {
|
|
newargs = make([]any, len(args))
|
|
copy(newargs, args)
|
|
}
|
|
newargs[i] = tsStr
|
|
}
|
|
if newargs == nil {
|
|
logf(format, args...)
|
|
return
|
|
}
|
|
logf(format, newargs...)
|
|
}
|
|
if envknob.Bool("TS_DEBUG_RAW_WGLOG") {
|
|
wrapper = logf
|
|
}
|
|
ret.DeviceLogger = &device.Logger{
|
|
Verbosef: logger.WithPrefix(wrapper, prefix+"[v2] "),
|
|
Errorf: logger.WithPrefix(wrapper, prefix),
|
|
}
|
|
return ret
|
|
}
|
|
|
|
// peerStringFor returns the Tailscale-conventional short string for
|
|
// wgString, if wgString is a wireguard-go-formatted peer string for a
|
|
// peer known to x.lookup. Results are memoized in x.cache for
|
|
// subsequent log lines.
|
|
func (x *Logger) peerStringFor(wgString string) (tsString string, ok bool) {
|
|
// Fast path: only strings shaped like wireguard-go's
|
|
// "peer(XXXX…YYYY)" output can be peer references; skip the cache
|
|
// and lookup call for anything else (e.g. arbitrary stringers used
|
|
// in unrelated log args).
|
|
if !strings.HasPrefix(wgString, "peer(") || !strings.HasSuffix(wgString, ")") {
|
|
return "", false
|
|
}
|
|
x.mu.Lock()
|
|
if v, ok := x.cache[wgString]; ok {
|
|
x.mu.Unlock()
|
|
return v, true
|
|
}
|
|
x.mu.Unlock()
|
|
|
|
// Call lookup without x.mu held so we don't have to reason about
|
|
// deadlocks between x.mu and any locks lookup acquires.
|
|
// In the worst case, two goroutines concurrently miss the cache
|
|
// for the same wgString and both call lookup; the redundant call
|
|
// is wasted but harmless.
|
|
tsString, ok = x.lookup(wgString)
|
|
if !ok {
|
|
return "", false
|
|
}
|
|
x.mu.Lock()
|
|
mak.Set(&x.cache, wgString, tsString)
|
|
x.mu.Unlock()
|
|
return tsString, true
|
|
}
|
|
|
|
// Invalidate clears the peer-string rewrite cache.
|
|
//
|
|
// Callers should invoke Invalidate when the set of wireguard-go peers
|
|
// has changed so that the next log line re-resolves peer references
|
|
// against the current lookup. It is safe to call concurrently with
|
|
// logging.
|
|
func (x *Logger) Invalidate() {
|
|
x.mu.Lock()
|
|
clear(x.cache)
|
|
x.mu.Unlock()
|
|
}
|