tstun,wgengine: add new datapath hooks for intercepting Connectors 2025

app connector packets

We introduce the Conn25PacketHooks interface to be used as a nil-able
field in userspaceEngine. The engine then plumbs through the functions
to the corresponding tstun.Wrapper intercepts.

The new intercepts run pre-filter when egressing toward WireGuard,
and post-filter when ingressing from WireGuard. This is preserve the
design invariant that the filter recognizes the traffic as interesting
app connector traffic.

This commit does not plumb through implementation of the interface, so
should be a functional no-op.

Fixes tailscale/corp#35985

Signed-off-by: Michael Ben-Ami <mzb@tailscale.com>
This commit is contained in:
Michael Ben-Ami
2025-12-11 15:31:15 -05:00
committed by mzbenami
parent 00e180cdd7
commit 5a5572e48a
2 changed files with 64 additions and 15 deletions
+44 -15
View File
@@ -143,8 +143,9 @@ type userspaceEngine struct {
trimmedNodes map[key.NodePublic]bool // set of node keys of peers currently excluded from wireguard config
sentActivityAt map[netip.Addr]*mono.Time // value is accessed atomically
destIPActivityFuncs map[netip.Addr]func()
lastStatusPollTime mono.Time // last time we polled the engine status
reconfigureVPN func() error // or nil
lastStatusPollTime mono.Time // last time we polled the engine status
reconfigureVPN func() error // or nil
conn25PacketHooks Conn25PacketHooks // or nil
mu sync.Mutex // guards following; see lock order comment below
netMap *netmap.NetworkMap // or nil
@@ -175,6 +176,19 @@ type BIRDClient interface {
Close() error
}
// Conn25PacketHooks are hooks for Connectors 2025 app connectors.
// They are meant to be wired into to corresponding hooks in the
// [tstun.Wrapper]. They may modify the packet (e.g., NAT), or drop
// invalid app connector traffic.
type Conn25PacketHooks interface {
// HandlePacketsFromTunDevice sends packets originating from the tun device
// for further Connectors 2025 app connectors processing.
HandlePacketsFromTunDevice(*packet.Parsed) filter.Response
// HandlePacketsFromWireguard sends packets originating from WireGuard
// for further Connectors 2025 app connectors processing.
HandlePacketsFromWireGuard(*packet.Parsed) filter.Response
}
// Config is the engine configuration.
type Config struct {
// Tun is the device used by the Engine to exchange packets with
@@ -247,6 +261,10 @@ type Config struct {
// TODO(creachadair): As of 2025-03-19 this is optional, but is intended to
// become required non-nil.
EventBus *eventbus.Bus
// Conn25PacketHooks, if non-nil, is used to hook packets for Connectors 2025
// app connector handling logic.
Conn25PacketHooks Conn25PacketHooks
}
// NewFakeUserspaceEngine returns a new userspace engine for testing.
@@ -348,19 +366,20 @@ func NewUserspaceEngine(logf logger.Logf, conf Config) (_ Engine, reterr error)
}
e := &userspaceEngine{
eventBus: conf.EventBus,
timeNow: mono.Now,
logf: logf,
reqCh: make(chan struct{}, 1),
waitCh: make(chan struct{}),
tundev: tsTUNDev,
router: rtr,
dialer: conf.Dialer,
confListenPort: conf.ListenPort,
birdClient: conf.BIRDClient,
controlKnobs: conf.ControlKnobs,
reconfigureVPN: conf.ReconfigureVPN,
health: conf.HealthTracker,
eventBus: conf.EventBus,
timeNow: mono.Now,
logf: logf,
reqCh: make(chan struct{}, 1),
waitCh: make(chan struct{}),
tundev: tsTUNDev,
router: rtr,
dialer: conf.Dialer,
confListenPort: conf.ListenPort,
birdClient: conf.BIRDClient,
controlKnobs: conf.ControlKnobs,
reconfigureVPN: conf.ReconfigureVPN,
health: conf.HealthTracker,
conn25PacketHooks: conf.Conn25PacketHooks,
}
if e.birdClient != nil {
@@ -434,6 +453,16 @@ func NewUserspaceEngine(logf logger.Logf, conf Config) (_ Engine, reterr error)
}
e.tundev.PreFilterPacketOutboundToWireGuardEngineIntercept = e.handleLocalPackets
if e.conn25PacketHooks != nil {
e.tundev.PreFilterPacketOutboundToWireGuardAppConnectorIntercept = func(p *packet.Parsed, _ *tstun.Wrapper) filter.Response {
return e.conn25PacketHooks.HandlePacketsFromTunDevice(p)
}
e.tundev.PostFilterPacketInboundFromWireGuardAppConnector = func(p *packet.Parsed, _ *tstun.Wrapper) filter.Response {
return e.conn25PacketHooks.HandlePacketsFromWireGuard(p)
}
}
if buildfeatures.HasDebug && envknob.BoolDefaultTrue("TS_DEBUG_CONNECT_FAILURES") {
if e.tundev.PreFilterPacketInboundFromWireGuard != nil {
return nil, errors.New("unexpected PreFilterIn already set")