mirror of
https://github.com/tailscale/tailscale.git
synced 2026-06-25 00:11:39 -04:00
net/tstun: invoke conn25 app connector hook on injected reads
The primary purpose is that return packets from the target app get properly SNATed on connectors with --tun=userspace-networking, matching the NAT behavior in the kernel tun path. This is also necessary but not sufficient for clients of connectors in userspace networking mode. The hook will DNAT MagicIPs, but won't actually be sent MagicIPs until conn25 app connector DNS works with userspace networking. Fixes tailscale/corp#43201 Signed-off-by: Michael Ben-Ami <mzb@tailscale.com>
This commit is contained in:
committed by
mzbenami
parent
77d2c87b17
commit
1b2062f3c1
@@ -1154,6 +1154,26 @@ func (t *Wrapper) injectedRead(res tunInjectedRead, outBuffs [][]byte, sizes []i
|
||||
}
|
||||
|
||||
invertGSOChecksum(pkt, gso)
|
||||
// Check if this is a packet for conn25-style app connectors,
|
||||
// and perform the necessary NAT. The main case that requires
|
||||
// NAT from netstack toward WireGuard is an SNAT on return traffic
|
||||
// from the target application on the internet, translating
|
||||
// the original server's source IP to the TransitIP.
|
||||
// The hook can also perform DNAT for client-originated traffic,
|
||||
// translating the destination MagicIP to a TransitIP, and rejects
|
||||
// MagicIPs that have not been approved for the client.
|
||||
//
|
||||
// Normal non-connector traffic is forwarded unmodified.
|
||||
//
|
||||
// Cross-tailnet conn25 app connector connections are not supported,
|
||||
// so at most one of this hook and the following pc.snat should modify the packet.
|
||||
if t.PreFilterPacketOutboundToWireGuardAppConnectorIntercept != nil {
|
||||
if r := t.PreFilterPacketOutboundToWireGuardAppConnectorIntercept(p, t); r.IsDrop() {
|
||||
metricPacketOut.Add(1)
|
||||
metricPacketOutDrop.Add(1)
|
||||
return 0, nil
|
||||
}
|
||||
}
|
||||
pc.snat(p)
|
||||
invertGSOChecksum(pkt, gso)
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
"tailscale.com/disco"
|
||||
"tailscale.com/net/netaddr"
|
||||
"tailscale.com/net/packet"
|
||||
"tailscale.com/net/packet/checksum"
|
||||
"tailscale.com/tstest"
|
||||
"tailscale.com/tstime/mono"
|
||||
"tailscale.com/types/ipproto"
|
||||
@@ -1120,3 +1121,36 @@ func TestInterceptOrdering(t *testing.T) {
|
||||
t.Errorf("got number of intercepts run in Read(): %d; want: %d", seq, numOutboundIntercepts)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInjectedReadCallsAppConnectorHook(t *testing.T) {
|
||||
var called bool
|
||||
hook := func(p *packet.Parsed, _ *Wrapper) filter.Response {
|
||||
called = true
|
||||
checksum.UpdateSrcAddr(p, netip.MustParseAddr("169.254.0.1"))
|
||||
return filter.Accept
|
||||
}
|
||||
|
||||
bus := eventbustest.NewBus(t)
|
||||
_, tun := newFakeTUN(t.Logf, bus, false)
|
||||
tun.PreFilterPacketOutboundToWireGuardAppConnectorIntercept = hook
|
||||
tun.Start()
|
||||
defer tun.Close()
|
||||
|
||||
if err := tun.InjectOutbound(udp4("145.53.32.10", "100.25.63.57", 80, 12345)); err != nil {
|
||||
t.Fatalf("InjectOutbound error: %v", err)
|
||||
}
|
||||
|
||||
var buf [MaxPacketSize]byte
|
||||
sizes := make([]int, 1)
|
||||
tun.Read([][]byte{buf[:]}, sizes, 0)
|
||||
|
||||
if !called {
|
||||
t.Error("app connector hook was not called in InjectOutbound")
|
||||
}
|
||||
|
||||
wantPkt := udp4("169.254.0.1", "100.25.63.57", 80, 12345)
|
||||
gotPkt := buf[:sizes[0]]
|
||||
if !bytes.Equal(wantPkt, gotPkt) {
|
||||
t.Errorf("packet mismatch\nwant:\t% x\ngot:\t% x", wantPkt, gotPkt)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user