net/tstun,wgengine/netstack: implement TCP GRO for local services (#13315)

Throughput improves substantially when measured via netstack loopback
(TS_DEBUG_NETSTACK_LOOPBACK_PORT).

Before (d21ebc2):
jwhited@i5-12400-2:~$ iperf3 -V -c 100.100.100.100
Starting Test: protocol: TCP, 1 streams, 131072 byte blocks
Test Complete. Summary Results:
[ ID] Interval           Transfer     Bitrate         Retr
[  5]   0.00-10.00  sec  5.77 GBytes  4.95 Gbits/sec    0 sender
[  5]   0.00-10.01  sec  5.77 GBytes  4.95 Gbits/sec      receiver

After:
jwhited@i5-12400-2:~$ iperf3 -V -c 100.100.100.100
Starting Test: protocol: TCP, 1 streams, 131072 byte blocks
Test Complete. Summary Results:
[ ID] Interval           Transfer     Bitrate         Retr
[  5]   0.00-10.00  sec  12.7 GBytes  10.9 Gbits/sec    0 sender
[  5]   0.00-10.00  sec  12.7 GBytes  10.9 Gbits/sec      receiver

Updates tailscale/corp#22754

Signed-off-by: Jordan Whited <jordan@tailscale.com>
This commit is contained in:
Jordan Whited
2024-08-29 11:37:48 -07:00
committed by GitHub
parent 71acf87830
commit 0926954cf5
4 changed files with 33 additions and 26 deletions

View File

@@ -725,9 +725,9 @@ func (ns *Impl) isLoopbackPort(port uint16) bool {
// handleLocalPackets is hooked into the tun datapath for packets leaving
// the host and arriving at tailscaled. This method returns filter.DropSilently
// to intercept a packet for handling, for instance traffic to quad-100.
func (ns *Impl) handleLocalPackets(p *packet.Parsed, t *tstun.Wrapper) filter.Response {
func (ns *Impl) handleLocalPackets(p *packet.Parsed, t *tstun.Wrapper, gro *gro.GRO) (filter.Response, *gro.GRO) {
if ns.ctx.Err() != nil {
return filter.DropSilently
return filter.DropSilently, gro
}
// Determine if we care about this local packet.
@@ -741,11 +741,11 @@ func (ns *Impl) handleLocalPackets(p *packet.Parsed, t *tstun.Wrapper) filter.Re
switch p.IPProto {
case ipproto.TCP:
if port := p.Dst.Port(); port != 53 && port != 80 && port != 8080 && !ns.isLoopbackPort(port) {
return filter.Accept
return filter.Accept, gro
}
case ipproto.UDP:
if port := p.Dst.Port(); port != 53 && !ns.isLoopbackPort(port) {
return filter.Accept
return filter.Accept, gro
}
}
case viaRange.Contains(dst):
@@ -759,7 +759,7 @@ func (ns *Impl) handleLocalPackets(p *packet.Parsed, t *tstun.Wrapper) filter.Re
if !shouldHandle {
// Unhandled means that we let the regular processing
// occur without doing anything ourselves.
return filter.Accept
return filter.Accept, gro
}
if debugNetstack() {
@@ -785,7 +785,7 @@ func (ns *Impl) handleLocalPackets(p *packet.Parsed, t *tstun.Wrapper) filter.Re
}
go ns.userPing(pingIP, pong, userPingDirectionInbound)
return filter.DropSilently
return filter.DropSilently, gro
}
// Fall through to writing inbound so netstack handles the
@@ -794,14 +794,14 @@ func (ns *Impl) handleLocalPackets(p *packet.Parsed, t *tstun.Wrapper) filter.Re
default:
// Not traffic to the service IP or a 4via6 IP, so we don't
// care about the packet; resume processing.
return filter.Accept
return filter.Accept, gro
}
if debugPackets {
ns.logf("[v2] service packet in (from %v): % x", p.Src, p.Buffer())
}
ns.linkEP.injectInbound(p)
return filter.DropSilently
gro = ns.linkEP.gro(p, gro)
return filter.DropSilently, gro
}
func (ns *Impl) DialContextTCP(ctx context.Context, ipp netip.AddrPort) (*gonet.TCPConn, error) {