2022-09-01 01:09:44 +00:00
package tailnet
import (
"context"
2023-08-15 01:38:37 +00:00
"encoding/binary"
2022-09-01 01:09:44 +00:00
"fmt"
"net"
2023-03-21 18:43:20 +00:00
"net/http"
2022-09-01 01:09:44 +00:00
"net/netip"
2023-08-08 17:56:08 +00:00
"os"
2023-04-20 22:58:22 +00:00
"strconv"
2022-09-01 01:09:44 +00:00
"sync"
"time"
2022-11-15 18:59:22 +00:00
"github.com/cenkalti/backoff/v4"
2022-09-01 01:09:44 +00:00
"github.com/google/uuid"
"golang.org/x/xerrors"
2023-04-18 22:53:11 +00:00
"gvisor.dev/gvisor/pkg/tcpip"
2022-09-01 01:09:44 +00:00
"gvisor.dev/gvisor/pkg/tcpip/adapters/gonet"
2023-08-15 19:26:56 +00:00
"tailscale.com/envknob"
2022-09-01 01:09:44 +00:00
"tailscale.com/ipn/ipnstate"
2023-02-10 03:43:18 +00:00
"tailscale.com/net/connstats"
2023-08-09 19:50:26 +00:00
"tailscale.com/net/netmon"
2022-09-01 01:09:44 +00:00
"tailscale.com/net/netns"
"tailscale.com/net/tsdial"
"tailscale.com/net/tstun"
"tailscale.com/tailcfg"
2023-08-09 19:50:26 +00:00
"tailscale.com/tsd"
2022-09-01 01:09:44 +00:00
"tailscale.com/types/key"
tslogger "tailscale.com/types/logger"
2022-11-18 22:46:53 +00:00
"tailscale.com/types/netlogtype"
2022-09-01 01:09:44 +00:00
"tailscale.com/wgengine"
2024-01-29 15:57:31 +00:00
"tailscale.com/wgengine/capture"
2022-09-01 01:09:44 +00:00
"tailscale.com/wgengine/magicsock"
"tailscale.com/wgengine/netstack"
"tailscale.com/wgengine/router"
2022-11-18 22:46:53 +00:00
"cdr.dev/slog"
2023-08-18 18:55:43 +00:00
"github.com/coder/coder/v2/cryptorand"
2024-01-22 07:07:50 +00:00
"github.com/coder/coder/v2/tailnet/proto"
2022-09-01 01:09:44 +00:00
)
2023-10-06 16:31:53 +00:00
var ErrConnClosed = xerrors . New ( "connection closed" )
2023-04-18 22:53:11 +00:00
const (
WorkspaceAgentSSHPort = 1
WorkspaceAgentReconnectingPTYPort = 2
WorkspaceAgentSpeedtestPort = 3
)
2023-08-08 17:56:08 +00:00
// EnvMagicsockDebugLogging enables super-verbose logging for the magicsock
// internals. A logger must be supplied to the connection with the debug level
// enabled.
//
// With this disabled, you still get a lot of output if you have a valid logger
// with the debug level enabled.
const EnvMagicsockDebugLogging = "CODER_MAGICSOCK_DEBUG_LOGGING"
2022-09-01 01:09:44 +00:00
func init ( ) {
2022-11-18 22:46:53 +00:00
// Globally disable network namespacing. All networking happens in
// userspace.
2022-09-01 01:09:44 +00:00
netns . SetEnabled ( false )
2023-08-15 19:26:56 +00:00
// Tailscale, by default, "trims" the set of peers down to ones that we are
// "actively" communicating with in an effort to save memory. Since
// Tailscale removed keep-alives, it seems like open but idle connections
// (SSH, port-forward, etc) can get trimmed fairly easily, causing hangs for
// a few seconds while the connection is setup again.
//
// Note that Tailscale.com's use case is very different from ours: in their
// use case, users create one persistent tailnet per device, and it allows
// connections to every other thing in Tailscale that belongs to them. The
// tailnet stays up as long as your laptop or phone is turned on.
//
// Our use case is different: for clients, it's a point-to-point connection
// to a single workspace, and lasts only as long as the connection. For
// agents, it's connections to a small number of clients (CLI or Coderd)
// that are being actively used by the end user.
envknob . Setenv ( "TS_DEBUG_TRIM_WIREGUARD" , "false" )
2022-09-01 01:09:44 +00:00
}
type Options struct {
2023-08-15 01:38:37 +00:00
ID uuid . UUID
2023-03-21 18:43:20 +00:00
Addresses [ ] netip . Prefix
DERPMap * tailcfg . DERPMap
DERPHeader * http . Header
2023-08-24 17:22:31 +00:00
// DERPForceWebSockets determines whether websockets is always used for DERP
// connections, rather than trying `Upgrade: derp` first and potentially
// falling back. This is useful for misbehaving proxies that prevent
// fallback due to odd behavior, like Azure App Proxy.
DERPForceWebSockets bool
2022-10-17 13:43:30 +00:00
// BlockEndpoints specifies whether P2P endpoints are blocked.
// If so, only DERPs can establish connections.
BlockEndpoints bool
Logger slog . Logger
2023-04-03 16:20:19 +00:00
ListenPort uint16
2022-09-01 01:09:44 +00:00
}
2023-08-15 01:38:37 +00:00
// NodeID creates a Tailscale NodeID from the last 8 bytes of a UUID. It ensures
// the returned NodeID is always positive.
func NodeID ( uid uuid . UUID ) tailcfg . NodeID {
id := int64 ( binary . BigEndian . Uint64 ( uid [ 8 : ] ) )
// ensure id is positive
y := id >> 63
id = ( id ^ y ) - y
return tailcfg . NodeID ( id )
}
2022-09-01 01:09:44 +00:00
// NewConn constructs a new Wireguard server that will accept connections from the addresses provided.
2023-02-24 11:11:28 +00:00
func NewConn ( options * Options ) ( conn * Conn , err error ) {
2022-09-01 01:09:44 +00:00
if options == nil {
options = & Options { }
}
if len ( options . Addresses ) == 0 {
return nil , xerrors . New ( "At least one IP range must be provided" )
}
2023-02-10 03:43:18 +00:00
2022-09-01 01:09:44 +00:00
nodePrivateKey := key . NewNode ( )
2023-08-15 01:38:37 +00:00
var nodeID tailcfg . NodeID
// If we're provided with a UUID, use it to populate our node ID.
if options . ID != uuid . Nil {
nodeID = NodeID ( options . ID )
} else {
uid , err := cryptorand . Int63 ( )
if err != nil {
return nil , xerrors . Errorf ( "generate node id: %w" , err )
}
nodeID = tailcfg . NodeID ( uid )
2022-09-01 01:09:44 +00:00
}
2023-08-15 01:38:37 +00:00
2023-08-09 19:50:26 +00:00
wireguardMonitor , err := netmon . New ( Logger ( options . Logger . Named ( "net.wgmonitor" ) ) )
2022-09-01 01:09:44 +00:00
if err != nil {
return nil , xerrors . Errorf ( "create wireguard link monitor: %w" , err )
}
2023-02-24 11:11:28 +00:00
defer func ( ) {
if err != nil {
wireguardMonitor . Close ( )
}
} ( )
2022-09-01 01:09:44 +00:00
dialer := & tsdial . Dialer {
2023-07-26 21:46:22 +00:00
Logf : Logger ( options . Logger . Named ( "net.tsdial" ) ) ,
2022-09-01 01:09:44 +00:00
}
2023-08-09 19:50:26 +00:00
sys := new ( tsd . System )
2023-07-26 21:46:22 +00:00
wireguardEngine , err := wgengine . NewUserspaceEngine ( Logger ( options . Logger . Named ( "net.wgengine" ) ) , wgengine . Config {
2023-08-09 19:50:26 +00:00
NetMon : wireguardMonitor ,
Dialer : dialer ,
ListenPort : options . ListenPort ,
SetSubsystem : sys . Set ,
2022-09-01 01:09:44 +00:00
} )
if err != nil {
return nil , xerrors . Errorf ( "create wgengine: %w" , err )
}
2023-02-24 11:11:28 +00:00
defer func ( ) {
if err != nil {
wireguardEngine . Close ( )
}
} ( )
2022-09-01 01:09:44 +00:00
dialer . UseNetstackForIP = func ( ip netip . Addr ) bool {
_ , ok := wireguardEngine . PeerForIP ( ip )
return ok
}
2023-08-09 19:50:26 +00:00
sys . Set ( wireguardEngine )
magicConn := sys . MagicSock . Get ( )
2023-08-24 17:22:31 +00:00
magicConn . SetDERPForceWebsockets ( options . DERPForceWebSockets )
2024-02-20 05:51:25 +00:00
magicConn . SetBlockEndpoints ( options . BlockEndpoints )
2023-03-21 18:43:20 +00:00
if options . DERPHeader != nil {
magicConn . SetDERPHeader ( options . DERPHeader . Clone ( ) )
}
2022-09-01 01:09:44 +00:00
2023-08-08 17:56:08 +00:00
if v , ok := os . LookupEnv ( EnvMagicsockDebugLogging ) ; ok {
vBool , err := strconv . ParseBool ( v )
if err != nil {
options . Logger . Debug ( context . Background ( ) , fmt . Sprintf ( "magicsock debug logging disabled due to invalid value %s=%q, use true or false" , EnvMagicsockDebugLogging , v ) )
} else {
magicConn . SetDebugLoggingEnabled ( vBool )
options . Logger . Debug ( context . Background ( ) , fmt . Sprintf ( "magicsock debug logging set by %s=%t" , EnvMagicsockDebugLogging , vBool ) )
}
} else {
options . Logger . Debug ( context . Background ( ) , fmt . Sprintf ( "magicsock debug logging disabled, use %s=true to enable" , EnvMagicsockDebugLogging ) )
}
2022-09-01 01:09:44 +00:00
// Update the keys for the magic connection!
err = magicConn . SetPrivateKey ( nodePrivateKey )
if err != nil {
return nil , xerrors . Errorf ( "set node private key: %w" , err )
}
netStack , err := netstack . Create (
2023-07-26 21:46:22 +00:00
Logger ( options . Logger . Named ( "net.netstack" ) ) ,
2023-08-09 19:50:26 +00:00
sys . Tun . Get ( ) ,
2023-07-12 22:37:31 +00:00
wireguardEngine ,
magicConn ,
dialer ,
2023-08-09 19:50:26 +00:00
sys . DNSManager . Get ( ) ,
2023-07-12 22:37:31 +00:00
)
2022-09-01 01:09:44 +00:00
if err != nil {
return nil , xerrors . Errorf ( "create netstack: %w" , err )
}
2023-07-12 22:37:31 +00:00
2022-09-01 01:09:44 +00:00
dialer . NetstackDialTCP = func ( ctx context . Context , dst netip . AddrPort ) ( net . Conn , error ) {
return netStack . DialContextTCP ( ctx , dst )
}
netStack . ProcessLocalIPs = true
wireguardEngine = wgengine . NewWatchdog ( wireguardEngine )
2024-01-22 07:07:50 +00:00
cfgMaps := newConfigMaps (
options . Logger ,
wireguardEngine ,
nodeID ,
nodePrivateKey ,
magicConn . DiscoPublicKey ( ) ,
)
cfgMaps . setAddresses ( options . Addresses )
2024-02-15 06:51:12 +00:00
if options . DERPMap != nil {
cfgMaps . setDERPMap ( options . DERPMap )
}
2024-01-22 07:07:50 +00:00
cfgMaps . setBlockEndpoints ( options . BlockEndpoints )
nodeUp := newNodeUpdater (
options . Logger ,
2023-07-12 22:37:31 +00:00
nil ,
2024-01-22 07:07:50 +00:00
nodeID ,
nodePrivateKey . Public ( ) ,
magicConn . DiscoPublicKey ( ) ,
)
nodeUp . setAddresses ( options . Addresses )
nodeUp . setBlockEndpoints ( options . BlockEndpoints )
wireguardEngine . SetStatusCallback ( nodeUp . setStatus )
wireguardEngine . SetNetInfoCallback ( nodeUp . setNetInfo )
magicConn . SetDERPForcedWebsocketCallback ( nodeUp . setDERPForcedWebsocket )
2023-07-12 22:37:31 +00:00
2022-09-01 01:09:44 +00:00
server := & Conn {
2024-01-22 07:07:50 +00:00
closed : make ( chan struct { } ) ,
logger : options . Logger ,
magicConn : magicConn ,
dialer : dialer ,
listeners : map [ listenKey ] * listener { } ,
tunDevice : sys . Tun . Get ( ) ,
netStack : netStack ,
wireguardMonitor : wireguardMonitor ,
2022-09-01 16:41:47 +00:00
wireguardRouter : & router . Config {
2024-01-22 07:07:50 +00:00
LocalAddrs : options . Addresses ,
2022-09-01 16:41:47 +00:00
} ,
wireguardEngine : wireguardEngine ,
2024-01-22 07:07:50 +00:00
configMaps : cfgMaps ,
nodeUpdater : nodeUp ,
2022-09-01 01:09:44 +00:00
}
2023-02-24 11:11:28 +00:00
defer func ( ) {
if err != nil {
_ = server . Close ( )
}
} ( )
2023-07-12 22:37:31 +00:00
2023-08-09 19:50:26 +00:00
netStack . GetTCPHandlerForFlow = server . forwardTCP
2023-02-24 11:11:28 +00:00
err = netStack . Start ( nil )
if err != nil {
return nil , xerrors . Errorf ( "start netstack: %w" , err )
}
2022-09-01 01:09:44 +00:00
return server , nil
}
2023-07-12 22:37:31 +00:00
func maskUUID ( uid uuid . UUID ) uuid . UUID {
// This is Tailscale's ephemeral service prefix. This can be changed easily
// later-on, because all of our nodes are ephemeral.
2022-09-01 01:09:44 +00:00
// fd7a:115c:a1e0
uid [ 0 ] = 0xfd
uid [ 1 ] = 0x7a
uid [ 2 ] = 0x11
uid [ 3 ] = 0x5c
uid [ 4 ] = 0xa1
uid [ 5 ] = 0xe0
2023-07-12 22:37:31 +00:00
return uid
}
// IP generates a random IP with a static service prefix.
func IP ( ) netip . Addr {
uid := maskUUID ( uuid . New ( ) )
2022-09-01 01:09:44 +00:00
return netip . AddrFrom16 ( uid )
}
2023-07-12 22:37:31 +00:00
// IP generates a new IP from a UUID.
func IPFromUUID ( uid uuid . UUID ) netip . Addr {
return netip . AddrFrom16 ( maskUUID ( uid ) )
}
2022-09-01 01:09:44 +00:00
// Conn is an actively listening Wireguard connection.
type Conn struct {
2024-01-22 07:07:50 +00:00
mutex sync . Mutex
closed chan struct { }
logger slog . Logger
2022-09-01 01:09:44 +00:00
2023-04-18 22:53:11 +00:00
dialer * tsdial . Dialer
tunDevice * tstun . Wrapper
2024-01-22 07:07:50 +00:00
configMaps * configMaps
nodeUpdater * nodeUpdater
2023-04-18 22:53:11 +00:00
netStack * netstack . Impl
magicConn * magicsock . Conn
2023-08-09 19:50:26 +00:00
wireguardMonitor * netmon . Monitor
2023-04-18 22:53:11 +00:00
wireguardRouter * router . Config
wireguardEngine wgengine . Engine
listeners map [ listenKey ] * listener
2022-09-01 01:09:44 +00:00
2023-02-10 03:43:18 +00:00
trafficStats * connstats . Statistics
2022-09-01 01:09:44 +00:00
}
2024-04-10 22:15:33 +00:00
func ( c * Conn ) SetTunnelDestination ( id uuid . UUID ) {
c . configMaps . setTunnelDestination ( id )
}
2024-03-08 05:29:54 +00:00
func ( c * Conn ) GetBlockEndpoints ( ) bool {
return c . configMaps . getBlockEndpoints ( ) && c . nodeUpdater . getBlockEndpoints ( )
}
2024-01-29 15:57:31 +00:00
func ( c * Conn ) InstallCaptureHook ( f capture . Callback ) {
c . mutex . Lock ( )
defer c . mutex . Unlock ( )
c . wireguardEngine . InstallCaptureHook ( f )
}
2023-08-08 17:56:08 +00:00
func ( c * Conn ) MagicsockSetDebugLoggingEnabled ( enabled bool ) {
c . magicConn . SetDebugLoggingEnabled ( enabled )
}
2023-07-12 22:37:31 +00:00
func ( c * Conn ) SetAddresses ( ips [ ] netip . Prefix ) error {
2024-01-22 07:07:50 +00:00
c . configMaps . setAddresses ( ips )
c . nodeUpdater . setAddresses ( ips )
2023-07-12 22:37:31 +00:00
return nil
}
2022-09-01 01:09:44 +00:00
func ( c * Conn ) SetNodeCallback ( callback func ( node * Node ) ) {
2024-01-22 07:07:50 +00:00
c . nodeUpdater . setCallback ( callback )
2022-09-01 01:09:44 +00:00
}
// SetDERPMap updates the DERPMap of a connection.
func ( c * Conn ) SetDERPMap ( derpMap * tailcfg . DERPMap ) {
2024-01-24 12:27:15 +00:00
c . configMaps . setDERPMap ( derpMap )
2022-09-01 01:09:44 +00:00
}
2023-08-24 17:22:31 +00:00
func ( c * Conn ) SetDERPForceWebSockets ( v bool ) {
2024-01-22 07:07:50 +00:00
c . logger . Info ( context . Background ( ) , "setting DERP Force Websockets" , slog . F ( "force_derp_websockets" , v ) )
2023-08-24 17:22:31 +00:00
c . magicConn . SetDERPForceWebsockets ( v )
}
2024-01-22 07:07:50 +00:00
// SetBlockEndpoints sets whether to block P2P endpoints. This setting
2023-06-21 22:02:05 +00:00
// will only apply to new peers.
func ( c * Conn ) SetBlockEndpoints ( blockEndpoints bool ) {
2024-01-22 07:07:50 +00:00
c . configMaps . setBlockEndpoints ( blockEndpoints )
c . nodeUpdater . setBlockEndpoints ( blockEndpoints )
2024-02-20 05:51:25 +00:00
c . magicConn . SetBlockEndpoints ( blockEndpoints )
2023-06-21 22:02:05 +00:00
}
2023-03-14 14:46:47 +00:00
// SetDERPRegionDialer updates the dialer to use for connecting to DERP regions.
func ( c * Conn ) SetDERPRegionDialer ( dialer func ( ctx context . Context , region * tailcfg . DERPRegion ) net . Conn ) {
c . magicConn . SetDERPRegionDialer ( dialer )
}
2024-01-22 07:07:50 +00:00
// UpdatePeers connects with a set of peers. This can be constantly updated,
// and peers will continually be reconnected as necessary.
func ( c * Conn ) UpdatePeers ( updates [ ] * proto . CoordinateResponse_PeerUpdate ) error {
2023-08-09 19:50:26 +00:00
if c . isClosed ( ) {
2023-10-06 16:31:53 +00:00
return ErrConnClosed
2023-08-09 19:50:26 +00:00
}
2024-01-22 07:07:50 +00:00
c . configMaps . updatePeers ( updates )
2022-09-01 01:09:44 +00:00
return nil
}
2024-01-22 11:26:20 +00:00
// SetAllPeersLost marks all peers lost; typically used when we disconnect from a coordinator.
func ( c * Conn ) SetAllPeersLost ( ) {
c . configMaps . setAllPeersLost ( )
}
2023-03-02 14:06:00 +00:00
// NodeAddresses returns the addresses of a node from the NetworkMap.
func ( c * Conn ) NodeAddresses ( publicKey key . NodePublic ) ( [ ] netip . Prefix , bool ) {
2024-01-22 07:07:50 +00:00
return c . configMaps . nodeAddresses ( publicKey )
2023-03-02 14:06:00 +00:00
}
2022-09-01 01:09:44 +00:00
// Status returns the current ipnstate of a connection.
func ( c * Conn ) Status ( ) * ipnstate . Status {
2024-01-22 07:07:50 +00:00
return c . configMaps . status ( )
2022-09-01 01:09:44 +00:00
}
2024-01-02 11:53:52 +00:00
// Ping sends a ping to the Wireguard engine.
2022-12-18 23:50:06 +00:00
// The bool returned is true if the ping was performed P2P.
2023-02-13 16:38:00 +00:00
func ( c * Conn ) Ping ( ctx context . Context , ip netip . Addr ) ( time . Duration , bool , * ipnstate . PingResult , error ) {
2024-01-22 13:37:15 +00:00
return c . pingWithType ( ctx , ip , tailcfg . PingDisco )
}
func ( c * Conn ) pingWithType ( ctx context . Context , ip netip . Addr , pt tailcfg . PingType ) ( time . Duration , bool , * ipnstate . PingResult , error ) {
2022-11-13 17:33:05 +00:00
errCh := make ( chan error , 1 )
2022-12-18 23:50:06 +00:00
prChan := make ( chan * ipnstate . PingResult , 1 )
2024-01-22 13:37:15 +00:00
go c . wireguardEngine . Ping ( ip , pt , func ( pr * ipnstate . PingResult ) {
2022-11-13 17:33:05 +00:00
if pr . Err != "" {
errCh <- xerrors . New ( pr . Err )
return
}
2022-12-18 23:50:06 +00:00
prChan <- pr
2022-11-13 17:33:05 +00:00
} )
select {
case err := <- errCh :
2023-02-13 16:38:00 +00:00
return 0 , false , nil , err
2022-11-13 17:33:05 +00:00
case <- ctx . Done ( ) :
2023-02-13 16:38:00 +00:00
return 0 , false , nil , ctx . Err ( )
2022-12-18 23:50:06 +00:00
case pr := <- prChan :
2023-02-13 16:38:00 +00:00
return time . Duration ( pr . LatencySeconds * float64 ( time . Second ) ) , pr . Endpoint != "" , pr , nil
2022-11-13 17:33:05 +00:00
}
}
2022-12-18 23:50:06 +00:00
// DERPMap returns the currently set DERP mapping.
func ( c * Conn ) DERPMap ( ) * tailcfg . DERPMap {
2024-01-22 07:07:50 +00:00
c . configMaps . L . Lock ( )
defer c . configMaps . L . Unlock ( )
return c . configMaps . derpMapLocked ( )
2023-06-21 22:02:05 +00:00
}
2022-11-13 17:33:05 +00:00
// AwaitReachable pings the provided IP continually until the
// address is reachable. It's the callers responsibility to provide
// a timeout, otherwise this function will block forever.
func ( c * Conn ) AwaitReachable ( ctx context . Context , ip netip . Addr ) bool {
2022-11-15 18:59:22 +00:00
ctx , cancel := context . WithCancel ( ctx )
defer cancel ( ) // Cancel all pending pings on exit.
completedCtx , completed := context . WithCancel ( context . Background ( ) )
defer completed ( )
2022-11-13 17:33:05 +00:00
run := func ( ) {
2022-11-15 18:59:22 +00:00
// Safety timeout, initially we'll have around 10-20 goroutines
// running in parallel. The exponential backoff will converge
// around ~1 ping / 30s, this means we'll have around 10-20
// goroutines pending towards the end as well.
ctx , cancel := context . WithTimeout ( ctx , 5 * time . Minute )
defer cancel ( )
2024-01-22 13:37:15 +00:00
// For reachability, we use TSMP ping, which pings at the IP layer, and
// therefore requires that wireguard and the netstack are up. If we
// don't wait for wireguard to be up, we could miss a handshake, and it
// might take 5 seconds for the handshake to be retried. A 5s initial
// round trip can set us up for poor TCP performance, since the initial
// round-trip-time sets the initial retransmit timeout.
_ , _ , _ , err := c . pingWithType ( ctx , ip , tailcfg . PingTSMP )
2022-11-13 17:33:05 +00:00
if err == nil {
completed ( )
}
}
2022-11-15 18:59:22 +00:00
eb := backoff . NewExponentialBackOff ( )
eb . MaxElapsedTime = 0
eb . InitialInterval = 50 * time . Millisecond
eb . MaxInterval = 30 * time . Second
// Consume the first interval since
// we'll fire off a ping immediately.
_ = eb . NextBackOff ( )
t := backoff . NewTicker ( eb )
defer t . Stop ( )
2022-11-13 17:33:05 +00:00
go run ( )
for {
select {
case <- completedCtx . Done ( ) :
return true
2022-11-15 18:59:22 +00:00
case <- t . C :
2022-11-13 17:33:05 +00:00
// Pings can take a while, so we can run multiple
// in parallel to return ASAP.
go run ( )
case <- ctx . Done ( ) :
return false
}
}
2022-09-01 01:09:44 +00:00
}
// Closed is a channel that ends when the connection has
// been closed.
func ( c * Conn ) Closed ( ) <- chan struct { } {
return c . closed
}
// Close shuts down the Wireguard connection.
func ( c * Conn ) Close ( ) error {
2024-01-22 07:07:50 +00:00
c . logger . Info ( context . Background ( ) , "closing tailnet Conn" )
c . configMaps . close ( )
c . nodeUpdater . close ( )
2022-09-01 01:09:44 +00:00
c . mutex . Lock ( )
select {
case <- c . closed :
2022-09-12 17:46:45 +00:00
c . mutex . Unlock ( )
2022-09-01 01:09:44 +00:00
return nil
default :
}
2022-09-12 17:46:45 +00:00
close ( c . closed )
c . mutex . Unlock ( )
2023-02-24 11:11:28 +00:00
var wg sync . WaitGroup
defer wg . Wait ( )
if c . trafficStats != nil {
wg . Add ( 1 )
go func ( ) {
defer wg . Done ( )
ctx , cancel := context . WithTimeout ( context . Background ( ) , 5 * time . Second )
defer cancel ( )
_ = c . trafficStats . Shutdown ( ctx )
} ( )
}
2022-09-01 01:09:44 +00:00
_ = c . netStack . Close ( )
2023-11-13 11:14:42 +00:00
c . logger . Debug ( context . Background ( ) , "closed netstack" )
2022-09-01 01:09:44 +00:00
_ = c . wireguardMonitor . Close ( )
2023-02-24 11:11:28 +00:00
_ = c . dialer . Close ( )
// Stops internals, e.g. tunDevice, magicConn and dnsManager.
2022-09-01 01:09:44 +00:00
c . wireguardEngine . Close ( )
2023-02-24 11:11:28 +00:00
c . mutex . Lock ( )
for _ , l := range c . listeners {
_ = l . closeNoLock ( )
2023-02-10 03:43:18 +00:00
}
2023-02-24 11:11:28 +00:00
c . listeners = nil
c . mutex . Unlock ( )
2022-09-01 01:09:44 +00:00
return nil
}
2022-09-12 17:46:45 +00:00
func ( c * Conn ) isClosed ( ) bool {
select {
case <- c . closed :
return true
default :
return false
}
}
2022-12-18 23:50:06 +00:00
// Node returns the last node that was sent to the node callback.
func ( c * Conn ) Node ( ) * Node {
2024-01-22 07:07:50 +00:00
c . nodeUpdater . L . Lock ( )
defer c . nodeUpdater . L . Unlock ( )
return c . nodeUpdater . nodeLocked ( )
2022-12-18 23:50:06 +00:00
}
2022-09-01 01:09:44 +00:00
// This and below is taken _mostly_ verbatim from Tailscale:
// https://github.com/tailscale/tailscale/blob/c88bd53b1b7b2fcf7ba302f2e53dd1ce8c32dad4/tsnet/tsnet.go#L459-L494
2023-04-18 22:53:11 +00:00
// Listen listens for connections only on the Tailscale network.
2022-09-01 01:09:44 +00:00
func ( c * Conn ) Listen ( network , addr string ) ( net . Listener , error ) {
host , port , err := net . SplitHostPort ( addr )
if err != nil {
2023-04-18 22:53:11 +00:00
return nil , xerrors . Errorf ( "tailnet: split host port for listen: %w" , err )
2022-09-01 01:09:44 +00:00
}
lk := listenKey { network , host , port }
ln := & listener {
s : c ,
key : lk ,
addr : addr ,
2022-09-12 17:46:45 +00:00
closed : make ( chan struct { } ) ,
conn : make ( chan net . Conn ) ,
2022-09-01 01:09:44 +00:00
}
c . mutex . Lock ( )
2022-09-12 17:46:45 +00:00
if c . isClosed ( ) {
c . mutex . Unlock ( )
2023-10-06 16:31:53 +00:00
return nil , ErrConnClosed
2022-09-12 17:46:45 +00:00
}
2022-09-01 01:09:44 +00:00
if c . listeners == nil {
c . listeners = map [ listenKey ] * listener { }
}
if _ , ok := c . listeners [ lk ] ; ok {
c . mutex . Unlock ( )
2023-04-18 22:53:11 +00:00
return nil , xerrors . Errorf ( "tailnet: listener already open for %s, %s" , network , addr )
2022-09-01 01:09:44 +00:00
}
c . listeners [ lk ] = ln
c . mutex . Unlock ( )
return ln , nil
}
func ( c * Conn ) DialContextTCP ( ctx context . Context , ipp netip . AddrPort ) ( * gonet . TCPConn , error ) {
2023-11-01 09:45:13 +00:00
c . logger . Debug ( ctx , "dial tcp" , slog . F ( "addr_port" , ipp ) )
2022-09-01 01:09:44 +00:00
return c . netStack . DialContextTCP ( ctx , ipp )
}
func ( c * Conn ) DialContextUDP ( ctx context . Context , ipp netip . AddrPort ) ( * gonet . UDPConn , error ) {
2023-11-01 09:45:13 +00:00
c . logger . Debug ( ctx , "dial udp" , slog . F ( "addr_port" , ipp ) )
2022-09-01 01:09:44 +00:00
return c . netStack . DialContextUDP ( ctx , ipp )
}
2023-10-09 15:41:26 +00:00
func ( c * Conn ) forwardTCP ( src , dst netip . AddrPort ) ( handler func ( net . Conn ) , opts [ ] tcpip . SettableSocketOption , intercept bool ) {
logger := c . logger . Named ( "tcp" ) . With ( slog . F ( "src" , src . String ( ) ) , slog . F ( "dst" , dst . String ( ) ) )
2022-09-01 01:09:44 +00:00
c . mutex . Lock ( )
2023-08-09 19:50:26 +00:00
ln , ok := c . listeners [ listenKey { "tcp" , "" , fmt . Sprint ( dst . Port ( ) ) } ]
2022-09-01 01:09:44 +00:00
c . mutex . Unlock ( )
if ! ok {
2023-08-09 19:50:26 +00:00
return nil , nil , false
2023-04-20 22:58:22 +00:00
}
2023-04-18 22:53:11 +00:00
// See: https://github.com/tailscale/tailscale/blob/c7cea825aea39a00aca71ea02bab7266afc03e7c/wgengine/netstack/netstack.go#L888
2023-08-09 19:50:26 +00:00
if dst . Port ( ) == WorkspaceAgentSSHPort || dst . Port ( ) == 22 {
opt := tcpip . KeepaliveIdleOption ( 72 * time . Hour )
2023-04-18 22:53:11 +00:00
opts = append ( opts , & opt )
}
2023-08-09 19:50:26 +00:00
return func ( conn net . Conn ) {
t := time . NewTimer ( time . Second )
defer t . Stop ( )
select {
case ln . conn <- conn :
2023-10-09 15:41:26 +00:00
logger . Info ( context . Background ( ) , "accepted connection" )
2023-08-09 19:50:26 +00:00
return
case <- ln . closed :
2023-10-09 15:41:26 +00:00
logger . Info ( context . Background ( ) , "listener closed; closing connection" )
2023-08-09 19:50:26 +00:00
case <- c . closed :
2023-10-09 15:41:26 +00:00
logger . Info ( context . Background ( ) , "tailnet closed; closing connection" )
2023-08-09 19:50:26 +00:00
case <- t . C :
2023-10-09 15:41:26 +00:00
logger . Info ( context . Background ( ) , "listener timed out accepting; closing connection" )
2023-08-09 19:50:26 +00:00
}
_ = conn . Close ( )
} , opts , true
2022-09-01 01:09:44 +00:00
}
2023-02-10 03:43:18 +00:00
// SetConnStatsCallback sets a callback to be called after maxPeriod or
// maxConns, whichever comes first. Multiple calls overwrites the callback.
func ( c * Conn ) SetConnStatsCallback ( maxPeriod time . Duration , maxConns int , dump func ( start , end time . Time , virtual , physical map [ netlogtype . Connection ] netlogtype . Counts ) ) {
connStats := connstats . NewStatistics ( maxPeriod , maxConns , dump )
2023-02-24 11:11:28 +00:00
shutdown := func ( s * connstats . Statistics ) {
ctx , cancel := context . WithTimeout ( context . Background ( ) , 5 * time . Second )
defer cancel ( )
_ = s . Shutdown ( ctx )
}
2023-02-10 03:43:18 +00:00
c . mutex . Lock ( )
2023-02-24 11:11:28 +00:00
if c . isClosed ( ) {
c . mutex . Unlock ( )
shutdown ( connStats )
return
}
2023-02-10 03:43:18 +00:00
old := c . trafficStats
c . trafficStats = connStats
c . mutex . Unlock ( )
// Make sure to shutdown the old callback.
if old != nil {
2023-02-24 11:11:28 +00:00
shutdown ( old )
2023-02-10 03:43:18 +00:00
}
c . tunDevice . SetStatistics ( connStats )
2022-11-18 22:46:53 +00:00
}
2023-04-26 18:01:49 +00:00
func ( c * Conn ) MagicsockServeHTTPDebug ( w http . ResponseWriter , r * http . Request ) {
c . magicConn . ServeHTTPDebug ( w , r )
}
2024-02-27 18:04:46 +00:00
// PeerDiagnostics is a checklist of human-readable conditions necessary to establish an encrypted
// tunnel to a peer via a Conn
type PeerDiagnostics struct {
// PreferredDERP is 0 if we are not connected to a DERP region. If non-zero, we are connected to
// the given region as our home or "preferred" DERP.
PreferredDERP int
DERPRegionNames map [ int ] string
// SentNode is true if we have successfully transmitted our local Node via the most recently set
// NodeCallback.
SentNode bool
// ReceivedNode is the last Node we received for the peer, or nil if we haven't received the node.
ReceivedNode * tailcfg . Node
// LastWireguardHandshake is the last time we completed a wireguard handshake
LastWireguardHandshake time . Time
// TODO: surface Discovery (disco) protocol problems
}
func ( c * Conn ) GetPeerDiagnostics ( peerID uuid . UUID ) PeerDiagnostics {
d := PeerDiagnostics { DERPRegionNames : make ( map [ int ] string ) }
c . nodeUpdater . fillPeerDiagnostics ( & d )
c . configMaps . fillPeerDiagnostics ( & d , peerID )
return d
}
2022-09-01 01:09:44 +00:00
type listenKey struct {
network string
host string
port string
}
type listener struct {
2022-09-12 17:46:45 +00:00
s * Conn
key listenKey
addr string
conn chan net . Conn
closed chan struct { }
2022-09-01 01:09:44 +00:00
}
func ( ln * listener ) Accept ( ) ( net . Conn , error ) {
2022-09-12 17:46:45 +00:00
var c net . Conn
select {
case c = <- ln . conn :
case <- ln . closed :
2023-04-18 22:53:11 +00:00
return nil , xerrors . Errorf ( "tailnet: %w" , net . ErrClosed )
2022-09-01 01:09:44 +00:00
}
return c , nil
}
func ( ln * listener ) Addr ( ) net . Addr { return addr { ln } }
func ( ln * listener ) Close ( ) error {
ln . s . mutex . Lock ( )
defer ln . s . mutex . Unlock ( )
return ln . closeNoLock ( )
}
func ( ln * listener ) closeNoLock ( ) error {
if v , ok := ln . s . listeners [ ln . key ] ; ok && v == ln {
delete ( ln . s . listeners , ln . key )
2022-09-12 17:46:45 +00:00
close ( ln . closed )
2022-09-01 01:09:44 +00:00
}
return nil
}
type addr struct { ln * listener }
func ( a addr ) Network ( ) string { return a . ln . key . network }
func ( a addr ) String ( ) string { return a . ln . addr }
// Logger converts the Tailscale logging function to use slog.
func Logger ( logger slog . Logger ) tslogger . Logf {
return tslogger . Logf ( func ( format string , args ... any ) {
2023-02-24 11:11:28 +00:00
slog . Helper ( )
2022-09-01 01:09:44 +00:00
logger . Debug ( context . Background ( ) , fmt . Sprintf ( format , args ... ) )
} )
}