wgengine/netstack: deliver self-addressed packets via loopback
When a tsnet.Server dials its own Tailscale IP, TCP SYN packets are silently dropped. In inject(), outbound packets with dst=self fail the shouldSendToHost check and fall through to WireGuard, which has no peer for the node's own address. Fix this by detecting self-addressed packets in inject() using isLocalIP and delivering them back into gVisor's network stack as inbound packets via a new DeliverLoopback method on linkEndpoint. The outbound packet must be re-serialized into a new PacketBuffer because outbound packets have their headers parsed into separate views, but DeliverNetworkPacket expects raw unparsed data. Updates #18829 Signed-off-by: James Tucker <james@tailscale.com>
This commit is contained in:
committed by
James Tucker
parent
30e12310f1
commit
0fb207c3d0
@@ -7,6 +7,7 @@ import (
|
||||
"context"
|
||||
"sync"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/buffer"
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
||||
@@ -198,6 +199,43 @@ func (ep *linkEndpoint) injectInbound(p *packet.Parsed) {
|
||||
pkt.DecRef()
|
||||
}
|
||||
|
||||
// DeliverLoopback delivers pkt back into gVisor's network stack as if it
|
||||
// arrived from the network, for self-addressed (loopback) packets. It takes
|
||||
// ownership of one reference count on pkt. The caller must not use pkt after
|
||||
// calling this method. It returns false if the dispatcher is not attached.
|
||||
//
|
||||
// Outbound packets from gVisor have their headers already parsed into separate
|
||||
// views (NetworkHeader, TransportHeader, Data). DeliverNetworkPacket expects
|
||||
// a raw unparsed packet, so we must re-serialize the packet into a new
|
||||
// PacketBuffer with all bytes in the payload for gVisor to parse on inbound.
|
||||
func (ep *linkEndpoint) DeliverLoopback(pkt *stack.PacketBuffer) bool {
|
||||
ep.mu.RLock()
|
||||
d := ep.dispatcher
|
||||
ep.mu.RUnlock()
|
||||
if d == nil {
|
||||
pkt.DecRef()
|
||||
return false
|
||||
}
|
||||
|
||||
// Serialize the outbound packet back to raw bytes.
|
||||
raw := stack.PayloadSince(pkt.NetworkHeader()).AsSlice()
|
||||
proto := pkt.NetworkProtocolNumber
|
||||
|
||||
// We're done with the original outbound packet.
|
||||
pkt.DecRef()
|
||||
|
||||
// Create a new PacketBuffer from the raw bytes for inbound delivery.
|
||||
newPkt := stack.NewPacketBuffer(stack.PacketBufferOptions{
|
||||
Payload: buffer.MakeWithData(raw),
|
||||
})
|
||||
newPkt.NetworkProtocolNumber = proto
|
||||
newPkt.RXChecksumValidated = true
|
||||
|
||||
d.DeliverNetworkPacket(proto, newPkt)
|
||||
newPkt.DecRef()
|
||||
return true
|
||||
}
|
||||
|
||||
// Attach saves the stack network-layer dispatcher for use later when packets
|
||||
// are injected.
|
||||
func (ep *linkEndpoint) Attach(dispatcher stack.NetworkDispatcher) {
|
||||
|
||||
Reference in New Issue
Block a user