@ -6,6 +6,7 @@ package wgengine
import (
import (
"bufio"
"bufio"
"context"
crand "crypto/rand"
crand "crypto/rand"
"errors"
"errors"
"fmt"
"fmt"
@ -48,6 +49,7 @@ import (
"tailscale.com/wgengine/filter"
"tailscale.com/wgengine/filter"
"tailscale.com/wgengine/magicsock"
"tailscale.com/wgengine/magicsock"
"tailscale.com/wgengine/monitor"
"tailscale.com/wgengine/monitor"
"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"
@ -84,6 +86,10 @@ const (
// status (as long as there's activity). See docs on its use below.
// status (as long as there's activity). See docs on its use below.
const statusPollInterval = 1 * time . Minute
const statusPollInterval = 1 * time . Minute
// networkLoggerUploadTimeout is the maximum timeout to wait when
// shutting down the network logger as it uploads the last network log messages.
const networkLoggerUploadTimeout = 5 * time . Second
type userspaceEngine struct {
type userspaceEngine struct {
logf logger . Logf
logf logger . Logf
wgLogger * wglog . Logger //a wireguard-go logging wrapper
wgLogger * wglog . Logger //a wireguard-go logging wrapper
@ -145,6 +151,9 @@ type userspaceEngine struct {
// value of the ICMP identifer and sequence number concatenated.
// value of the ICMP identifer and sequence number concatenated.
icmpEchoResponseCallback map [ uint32 ] func ( )
icmpEchoResponseCallback map [ uint32 ] func ( )
// networkLogger logs statistics about network connections.
networkLogger netlog . Logger
// Lock ordering: magicsock.Conn.mu, wgLock, then mu.
// Lock ordering: magicsock.Conn.mu, wgLock, then mu.
}
}
@ -872,6 +881,12 @@ func (e *userspaceEngine) Reconfig(cfg *wgcfg.Config, routerCfg *router.Config,
if ! engineChanged && ! routerChanged && listenPort == e . magicConn . LocalPort ( ) && ! isSubnetRouterChanged {
if ! engineChanged && ! routerChanged && listenPort == e . magicConn . LocalPort ( ) && ! isSubnetRouterChanged {
return ErrNoChanges
return ErrNoChanges
}
}
newLogIDs := cfg . NetworkLogging
oldLogIDs := e . lastCfgFull . NetworkLogging
netLogIDsNowValid := ! newLogIDs . NodeID . IsZero ( ) && ! newLogIDs . DomainID . IsZero ( )
netLogIDsWasValid := ! oldLogIDs . NodeID . IsZero ( ) && ! oldLogIDs . DomainID . IsZero ( )
netLogIDsChanged := netLogIDsNowValid && netLogIDsWasValid && newLogIDs != oldLogIDs
netLogRunning := netLogIDsNowValid && ! routerCfg . Equal ( & router . Config { } )
// TODO(bradfitz,danderson): maybe delete this isDNSIPOverTailscale
// TODO(bradfitz,danderson): maybe delete this isDNSIPOverTailscale
// field and delete the resolver.ForwardLinkSelector hook and
// field and delete the resolver.ForwardLinkSelector hook and
@ -921,8 +936,31 @@ func (e *userspaceEngine) Reconfig(cfg *wgcfg.Config, routerCfg *router.Config,
return err
return err
}
}
// Shutdown the network logger because the IDs changed.
// Let it be started back up by subsequent logic.
if netLogIDsChanged && e . networkLogger . Running ( ) {
e . logf ( "wgengine: Reconfig: shutting down network logger" )
ctx , cancel := context . WithTimeout ( context . Background ( ) , networkLoggerUploadTimeout )
defer cancel ( )
if err := e . networkLogger . Shutdown ( ctx ) ; err != nil {
e . logf ( "wgengine: Reconfig: error shutting down network logger: %v" , err )
}
}
// Startup the network logger.
// Do this before configuring the router so that we capture initial packets.
if netLogRunning && ! e . networkLogger . Running ( ) {
nid := cfg . NetworkLogging . NodeID
tid := cfg . NetworkLogging . DomainID
e . logf ( "wgengine: Reconfig: starting up network logger (node:%s tailnet:%s)" , nid . Public ( ) , tid . Public ( ) )
if err := e . networkLogger . Startup ( nid , tid , e . tundev ) ; err != nil {
e . logf ( "wgengine: Reconfig: error starting up network logger: %v" , err )
}
}
if routerChanged {
if routerChanged {
e . logf ( "wgengine: Reconfig: configuring router" )
e . logf ( "wgengine: Reconfig: configuring router" )
e . networkLogger . ReconfigRoutes ( routerCfg )
err := e . router . Set ( routerCfg )
err := e . router . Set ( routerCfg )
health . SetRouterHealth ( err )
health . SetRouterHealth ( err )
if err != nil {
if err != nil {
@ -939,6 +977,18 @@ func (e *userspaceEngine) Reconfig(cfg *wgcfg.Config, routerCfg *router.Config,
}
}
}
}
// Shutdown the network logger.
// Do this after configuring the router so that we capture final packets.
// This attempts to flush out any log messages and may block.
if ! netLogRunning && e . networkLogger . Running ( ) {
e . logf ( "wgengine: Reconfig: shutting down network logger" )
ctx , cancel := context . WithTimeout ( context . Background ( ) , networkLoggerUploadTimeout )
defer cancel ( )
if err := e . networkLogger . Shutdown ( ctx ) ; err != nil {
e . logf ( "wgengine: Reconfig: error shutting down network logger: %v" , err )
}
}
if isSubnetRouterChanged && e . birdClient != nil {
if isSubnetRouterChanged && e . birdClient != nil {
e . logf ( "wgengine: Reconfig: configuring BIRD" )
e . logf ( "wgengine: Reconfig: configuring BIRD" )
var err error
var err error
@ -1092,6 +1142,12 @@ func (e *userspaceEngine) Close() {
e . birdClient . Close ( )
e . birdClient . Close ( )
}
}
close ( e . waitCh )
close ( e . waitCh )
ctx , cancel := context . WithTimeout ( context . Background ( ) , networkLoggerUploadTimeout )
defer cancel ( )
if err := e . networkLogger . Shutdown ( ctx ) ; err != nil {
e . logf ( "wgengine: Close: error shutting down network logger: %v" , err )
}
}
}
func ( e * userspaceEngine ) Wait ( ) {
func ( e * userspaceEngine ) Wait ( ) {