ipn/ipnlocal: move last unconditional gvisor import, complete ts_omit_netstack support

Fixes #17283

Change-Id: Ia84d269683e4a68d7d10562561204934eeaf53bb
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
Brad Fitzpatrick
2025-09-26 13:05:20 -07:00
committed by Brad Fitzpatrick
parent eaecc0be54
commit 87ee0f4e98
7 changed files with 91 additions and 79 deletions
-60
View File
@@ -38,7 +38,6 @@ import (
"go4.org/mem"
"go4.org/netipx"
"golang.org/x/net/dns/dnsmessage"
"gvisor.dev/gvisor/pkg/tcpip"
"tailscale.com/appc"
"tailscale.com/client/tailscale/apitype"
"tailscale.com/clientupdate"
@@ -4643,65 +4642,6 @@ var (
hookServeClearVIPServicesTCPPortsInterceptedLocked feature.Hook[func(*LocalBackend)]
)
// TCPHandlerForDst returns a TCP handler for connections to dst, or nil if
// no handler is needed. It also returns a list of TCP socket options to
// apply to the socket before calling the handler.
// TCPHandlerForDst is called both for connections to our node's local IP
// as well as to the service IP (quad 100).
func (b *LocalBackend) TCPHandlerForDst(src, dst netip.AddrPort) (handler func(c net.Conn) error, opts []tcpip.SettableSocketOption) {
// First handle internal connections to the service IP
hittingServiceIP := dst.Addr() == magicDNSIP || dst.Addr() == magicDNSIPv6
if hittingServiceIP {
switch dst.Port() {
case 80:
// TODO(mpminardi): do we want to show an error message if the web client
// has been disabled instead of the more "basic" web UI?
if b.ShouldRunWebClient() {
return b.handleWebClientConn, opts
}
return b.HandleQuad100Port80Conn, opts
case DriveLocalPort:
return b.handleDriveConn, opts
}
}
if f, ok := hookServeTCPHandlerForVIPService.GetOk(); ok {
if handler := f(b, dst, src); handler != nil {
return handler, opts
}
}
// Then handle external connections to the local IP.
if !b.isLocalIP(dst.Addr()) {
return nil, nil
}
if dst.Port() == 22 && b.ShouldRunSSH() {
// Use a higher keepalive idle time for SSH connections, as they are
// typically long lived and idle connections are more likely to be
// intentional. Ideally we would turn this off entirely, but we can't
// tell the difference between a long lived connection that is idle
// vs a connection that is dead because the peer has gone away.
// We pick 72h as that is typically sufficient for a long weekend.
opts = append(opts, ptr.To(tcpip.KeepaliveIdleOption(72*time.Hour)))
return b.handleSSHConn, opts
}
// TODO(will,sonia): allow customizing web client port ?
if dst.Port() == webClientPort && b.ShouldExposeRemoteWebClient() {
return b.handleWebClientConn, opts
}
if port, ok := b.GetPeerAPIPort(dst.Addr()); ok && dst.Port() == port {
return func(c net.Conn) error {
b.handlePeerAPIConn(src, dst, c)
return nil
}, opts
}
if f, ok := hookTCPHandlerForServe.GetOk(); ok {
if handler := f(b, dst.Port(), src, nil); handler != nil {
return handler, opts
}
}
return nil, nil
}
func (b *LocalBackend) handleDriveConn(conn net.Conn) error {
fs, ok := b.sys.DriveForLocal.GetOK()
if !ok || !b.DriveAccessEnabled() {
+74
View File
@@ -0,0 +1,74 @@
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
//go:build !ts_omit_netstack
package ipnlocal
import (
"net"
"net/netip"
"time"
"gvisor.dev/gvisor/pkg/tcpip"
"tailscale.com/types/ptr"
)
// TCPHandlerForDst returns a TCP handler for connections to dst, or nil if
// no handler is needed. It also returns a list of TCP socket options to
// apply to the socket before calling the handler.
// TCPHandlerForDst is called both for connections to our node's local IP
// as well as to the service IP (quad 100).
func (b *LocalBackend) TCPHandlerForDst(src, dst netip.AddrPort) (handler func(c net.Conn) error, opts []tcpip.SettableSocketOption) {
// First handle internal connections to the service IP
hittingServiceIP := dst.Addr() == magicDNSIP || dst.Addr() == magicDNSIPv6
if hittingServiceIP {
switch dst.Port() {
case 80:
// TODO(mpminardi): do we want to show an error message if the web client
// has been disabled instead of the more "basic" web UI?
if b.ShouldRunWebClient() {
return b.handleWebClientConn, opts
}
return b.HandleQuad100Port80Conn, opts
case DriveLocalPort:
return b.handleDriveConn, opts
}
}
if f, ok := hookServeTCPHandlerForVIPService.GetOk(); ok {
if handler := f(b, dst, src); handler != nil {
return handler, opts
}
}
// Then handle external connections to the local IP.
if !b.isLocalIP(dst.Addr()) {
return nil, nil
}
if dst.Port() == 22 && b.ShouldRunSSH() {
// Use a higher keepalive idle time for SSH connections, as they are
// typically long lived and idle connections are more likely to be
// intentional. Ideally we would turn this off entirely, but we can't
// tell the difference between a long lived connection that is idle
// vs a connection that is dead because the peer has gone away.
// We pick 72h as that is typically sufficient for a long weekend.
opts = append(opts, ptr.To(tcpip.KeepaliveIdleOption(72*time.Hour)))
return b.handleSSHConn, opts
}
// TODO(will,sonia): allow customizing web client port ?
if dst.Port() == webClientPort && b.ShouldExposeRemoteWebClient() {
return b.handleWebClientConn, opts
}
if port, ok := b.GetPeerAPIPort(dst.Addr()); ok && dst.Port() == port {
return func(c net.Conn) error {
b.handlePeerAPIConn(src, dst, c)
return nil
}, opts
}
if f, ok := hookTCPHandlerForServe.GetOk(); ok {
if handler := f(b, dst.Port(), src, nil); handler != nil {
return handler, opts
}
}
return nil, nil
}