mirror of
https://github.com/tailscale/tailscale.git
synced 2025-12-22 16:46:29 +00:00
[DRAFT] appc,wgengine: sketch how connectors 2025 hooks into the
datapath
This commit is contained in:
127
appc/conn25.go
127
appc/conn25.go
@@ -7,7 +7,10 @@ import (
|
||||
"net/netip"
|
||||
"sync"
|
||||
|
||||
"tailscale.com/net/packet"
|
||||
"tailscale.com/net/packet/checksum"
|
||||
"tailscale.com/tailcfg"
|
||||
"tailscale.com/wgengine/filter"
|
||||
)
|
||||
|
||||
// Conn25 holds the developing state for the as yet nascent next generation app connector.
|
||||
@@ -108,3 +111,127 @@ type ConnectorTransitIPResponse struct {
|
||||
// correspond to the order of [ConnectorTransitIPRequest.TransitIPs].
|
||||
TransitIPs []TransitIPResponse `json:"transitIPs,omitempty"`
|
||||
}
|
||||
|
||||
// DatapathHandler provides methods to intercept, mangle, and filter packets
|
||||
// in the datapath for app connector purposes.
|
||||
type DatapathHandler interface {
|
||||
// HandleLocalTraffic intercepts traffic from the local network stack, e.g. the tun device, and
|
||||
// determines if the traffic is app connector traffic that should be forwarded to a connector,
|
||||
// or is return traffic that should be forwarded back to the originating client. Valid packets may
|
||||
// be altered, e.g. NAT, and invalid packets may be dropped.
|
||||
HandleLocalTraffic(*packet.Parsed) filter.Response
|
||||
|
||||
// HandleTunnelTraffic intercepts traffic from the wireguard tunnel and determines if the traffic
|
||||
// is app connector traffic that should be forwarded to an application destination or back to the
|
||||
// local network stack. Valid packets may be altered, e.g. NAT, and invalid packets may be dropped.
|
||||
HandleTunnelTraffic(*packet.Parsed) filter.Response
|
||||
}
|
||||
|
||||
// datapathHandler is the main implementation of DatapathHandler.
|
||||
type datapathHandler struct {
|
||||
// conn25 *Conn25 perhaps
|
||||
// flowTable Flowtable perhaps
|
||||
}
|
||||
|
||||
func NewDatpathHandler() DatapathHandler {
|
||||
return &datapathHandler{}
|
||||
}
|
||||
|
||||
func (dh *datapathHandler) HandleLocalTraffic(p *packet.Parsed) filter.Response {
|
||||
// Connector-bound traffic.
|
||||
if dh.dstIPIsMagicIP(p) {
|
||||
return dh.processClientToConnector(p)
|
||||
}
|
||||
|
||||
// Return traffic from external application.
|
||||
if dh.selfIsConnector() && dh.isConnectorReturnTraffic(p) {
|
||||
return dh.processConnectorToClient(p)
|
||||
}
|
||||
// if controller client with flow in flow table, find address for source nat. If not, forward along.
|
||||
|
||||
return filter.Accept
|
||||
}
|
||||
|
||||
func (dh *datapathHandler) HandleTunnelTraffic(p *packet.Parsed) filter.Response {
|
||||
// Return traffic from connector, source is a Transit IP.
|
||||
if dh.srcIsTransitIP(p) {
|
||||
return dh.processClientFromConnector(p)
|
||||
}
|
||||
|
||||
// Outgoing traffic for an external application. Destination is Transit IP.
|
||||
if dh.selfIsConnector() && dh.dstIPIsTransitIP(p) {
|
||||
return dh.processConnectorFromClient(p)
|
||||
}
|
||||
return filter.Accept
|
||||
}
|
||||
|
||||
// processClientToConnector consults the flow table to determine which connector to send the packet to,
|
||||
// and if this is a new flow, runs the connector selection algorithm, and installs a new flow.
|
||||
// If the packet is valid, we DNAT from the Magic IP to the Transit IP.
|
||||
// If there is no flow or the packet is otherwise invalid, we drop the packet.
|
||||
func (dh *datapathHandler) processClientToConnector(p *packet.Parsed) filter.Response {
|
||||
// TODO: implement
|
||||
// TODO: we could do magic IP validation here as well
|
||||
|
||||
// This is just an example of how to do the NAT, when we need it.
|
||||
transitIP := netip.AddrFrom4([4]byte{169, 254, 100, 1})
|
||||
checksum.UpdateDstAddr(p, transitIP)
|
||||
|
||||
return filter.Drop
|
||||
}
|
||||
|
||||
// processConnectorToClient consults the flow table on a connector to determine which client
|
||||
// to send the return traffic to.
|
||||
// If the packet is valid, we SNAT the external application IP to the Transit IP.
|
||||
// If there is no flow or the packet is otherwise invalid, we drop the packet.
|
||||
func (dh *datapathHandler) processConnectorToClient(p *packet.Parsed) filter.Response {
|
||||
// TODO: implement
|
||||
return filter.Drop
|
||||
}
|
||||
|
||||
// processClientFromConnector consults the flow table to validate that the packet should
|
||||
// be forwarded back to the local network stack.
|
||||
// We SNAT the Transit IP back to the Magic IP.
|
||||
// If there is no flow or the packet is otherwise invalid, we drop the packet.
|
||||
func (dh *datapathHandler) processClientFromConnector(p *packet.Parsed) filter.Response {
|
||||
// TODO: implement
|
||||
return filter.Drop
|
||||
}
|
||||
|
||||
// processConnectorFromClient consults the flow table to see if this packet is part of
|
||||
// an existing outbound flow to an application, or a new flow should be installed.
|
||||
// If the packet is valid, we DNAT from the Transit IP to the external application IP.
|
||||
// If there is no flow or the packet is otherwise invalid, we drop the packet.
|
||||
func (dh *datapathHandler) processConnectorFromClient(p *packet.Parsed) filter.Response {
|
||||
// TODO: implement
|
||||
return filter.Drop
|
||||
}
|
||||
|
||||
// dstIPIsMagicIP returns whether the destination IP address in p is Magic IP,
|
||||
// which could indicate interesting traffic for outbound traffic from a client to a connector.
|
||||
func (dh *datapathHandler) dstIPIsMagicIP(p *packet.Parsed) bool {
|
||||
// TODO: implement
|
||||
// TODO: we could do magic IP validation here as well
|
||||
return false
|
||||
}
|
||||
|
||||
func (dh *datapathHandler) srcIsTransitIP(p *packet.Parsed) bool {
|
||||
// TODO: implement
|
||||
return false
|
||||
}
|
||||
|
||||
func (dh *datapathHandler) dstIPIsTransitIP(p *packet.Parsed) bool {
|
||||
// TODO: implement
|
||||
return false
|
||||
}
|
||||
|
||||
// selfIsConnector returns whether this client is running on an app connector.
|
||||
func (dh *datapathHandler) selfIsConnector() bool {
|
||||
// TODO: implement
|
||||
return false
|
||||
}
|
||||
|
||||
func (dh *datapathHandler) isConnectorReturnTraffic(p *packet.Parsed) bool {
|
||||
// TODO: implement
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
|
||||
"github.com/tailscale/wireguard-go/device"
|
||||
"github.com/tailscale/wireguard-go/tun"
|
||||
"tailscale.com/appc"
|
||||
"tailscale.com/control/controlknobs"
|
||||
"tailscale.com/drive"
|
||||
"tailscale.com/envknob"
|
||||
@@ -164,6 +165,10 @@ type userspaceEngine struct {
|
||||
// networkLogger logs statistics about network connections.
|
||||
networkLogger netlog.Logger
|
||||
|
||||
// appcDatapathHander intercepts, and possibly mangles and filters packets
|
||||
// for app connector operation.
|
||||
appcDatapathHandler appc.DatapathHandler
|
||||
|
||||
// Lock ordering: magicsock.Conn.mu, wgLock, then mu.
|
||||
}
|
||||
|
||||
@@ -430,6 +435,8 @@ func NewUserspaceEngine(logf logger.Logf, conf Config) (_ Engine, reterr error)
|
||||
|
||||
if conf.RespondToPing {
|
||||
e.tundev.PostFilterPacketInboundFromWireGuard = echoRespondToAll
|
||||
} else {
|
||||
e.tundev.PostFilterPacketInboundFromWireGuard = e.handleTunnelPackets
|
||||
}
|
||||
e.tundev.PreFilterPacketOutboundToWireGuardEngineIntercept = e.handleLocalPackets
|
||||
|
||||
@@ -623,9 +630,22 @@ func (e *userspaceEngine) handleLocalPackets(p *packet.Parsed, t *tstun.Wrapper)
|
||||
}
|
||||
}
|
||||
|
||||
if e.appcDatapathHandler != nil {
|
||||
return e.appcDatapathHandler.HandleLocalTraffic(p)
|
||||
}
|
||||
|
||||
return filter.Accept
|
||||
}
|
||||
|
||||
// handleTunnelPackets inspects packets coming from the wireguard tunnel stack and have passed through
|
||||
// the main filter. It intercepts and filters packets before delivering tun device wrapper..
|
||||
func (e *userspaceEngine) handleTunnelPackets(p *packet.Parsed, t *tstun.Wrapper, gro *gro.GRO) (filter.Response, *gro.GRO) {
|
||||
if e.appcDatapathHandler != nil {
|
||||
return e.appcDatapathHandler.HandleTunnelTraffic(p), gro
|
||||
}
|
||||
return filter.Accept, gro
|
||||
}
|
||||
|
||||
var debugTrimWireguard = envknob.RegisterOptBool("TS_DEBUG_TRIM_WIREGUARD")
|
||||
|
||||
// forceFullWireguardConfig reports whether we should give wireguard our full
|
||||
|
||||
Reference in New Issue
Block a user