Skip to content

Commit 86ba201

Browse files
authored
Merge pull request #46 from coder/spike/tcp-buf-sack-norack-cubic
feat: set large TCP buffers, SACK, and CUBIC congestion control
2 parents 3788ab8 + 99a59e7 commit 86ba201

File tree

1 file changed

+54
-7
lines changed

1 file changed

+54
-7
lines changed

wgengine/netstack/netstack.go

Lines changed: 54 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,27 @@ const nicID = 1
153153
// one day making the MTU more dynamic.
154154
const maxUDPPacketSize = 1500
155155

156+
const (
157+
megabytes = 1024 * 1024
158+
// recvBufSize is the size in bytes for TCP receive buffers. 6MiB is the usual maximum in
159+
// Linux, but here we set it as the default, because unlike Linux, gVisor does not dynamically
160+
// resize the buffer based on utilization. The channel that connects gVisor to Wireguard is 512
161+
// packets and Wireguard encrypt and decrypt buffers are 1024 packets each, so we could queue
162+
// 2.5k packets (over 2MiB), even before counting packets in flight on the network. The TCP
163+
// window is set to half the recv buffer, or 3 MiB in this case. Since TCP will only send this
164+
// much un-ACK'd data, this corresponds to max throughput of 3MiB per RTT (for example, 10 ms
165+
// RTT is 300 MiB/s or 2.4 Gbit/s).
166+
recvBufSize = 6 * megabytes
167+
// sendBufSize is the size in bytes for the TCP send buffers. 4MiB is the usual maximum in
168+
// Linux. The send buffer is used for both unsent and un-ACK'd data, so it is important that
169+
// it is greater than half of the recvBufSize so that there is still room for unsent data from
170+
// the application.
171+
sendBufSize = 4 * megabytes
172+
// CUBIC congestion control is the default in Windows, Linux, and MacOS, and generally achieves
173+
// better throughput on large, long networks.
174+
congestionControlCubic = "cubic"
175+
)
176+
156177
// Create creates and populates a new Impl.
157178
func Create(logf logger.Logf, tundev *tstun.Wrapper, e wgengine.Engine, mc *magicsock.Conn, dialer *tsdial.Dialer, dns *dns.Manager) (*Impl, error) {
158179
if mc == nil {
@@ -174,13 +195,41 @@ func Create(logf logger.Logf, tundev *tstun.Wrapper, e wgengine.Engine, mc *magi
174195
NetworkProtocols: []stack.NetworkProtocolFactory{ipv4.NewProtocol, ipv6.NewProtocol},
175196
TransportProtocols: []stack.TransportProtocolFactory{tcp.NewProtocol, udp.NewProtocol, icmp.NewProtocol4, icmp.NewProtocol6},
176197
})
177-
// Issue: https://github.com/coder/coder/issues/7388
178-
//
179-
/*sackEnabledOpt := tcpip.TCPSACKEnabled(true) // TCP SACK is disabled by default
198+
199+
sackEnabledOpt := tcpip.TCPSACKEnabled(true) // TCP SACK is disabled by default
180200
tcpipErr := ipstack.SetTransportProtocolOption(tcp.ProtocolNumber, &sackEnabledOpt)
181201
if tcpipErr != nil {
182202
return nil, fmt.Errorf("could not enable TCP SACK: %v", tcpipErr)
183-
}*/
203+
}
204+
soRecv := tcpip.TCPReceiveBufferSizeRangeOption{
205+
Min: recvBufSize,
206+
Default: recvBufSize,
207+
Max: recvBufSize,
208+
}
209+
tcpipErr = ipstack.SetTransportProtocolOption(tcp.ProtocolNumber, &soRecv)
210+
if tcpipErr != nil {
211+
return nil, fmt.Errorf("could not set recv buf size: %v", tcpipErr)
212+
}
213+
soSend := tcpip.TCPSendBufferSizeRangeOption{
214+
Min: sendBufSize,
215+
Default: sendBufSize,
216+
Max: sendBufSize,
217+
}
218+
tcpipErr = ipstack.SetTransportProtocolOption(tcp.ProtocolNumber, &soSend)
219+
if tcpipErr != nil {
220+
return nil, fmt.Errorf("could not set send buf size: %v", tcpipErr)
221+
}
222+
rack := tcpip.TCPRecovery(0) // Disable RACK
223+
tcpipErr = ipstack.SetTransportProtocolOption(tcp.ProtocolNumber, &rack)
224+
if tcpipErr != nil {
225+
return nil, fmt.Errorf("could not disable RACK: %v", tcpipErr)
226+
}
227+
cc := tcpip.CongestionControlOption(congestionControlCubic)
228+
tcpipErr = ipstack.SetTransportProtocolOption(tcp.ProtocolNumber, &cc)
229+
if tcpipErr != nil {
230+
return nil, fmt.Errorf("could not set congestion control: %v", tcpipErr)
231+
}
232+
184233
linkEP := &protectedLinkEndpoint{Endpoint: channel.New(512, tstun.DefaultMTU(), "")}
185234
if tcpipProblem := ipstack.CreateNIC(nicID, linkEP); tcpipProblem != nil {
186235
return nil, fmt.Errorf("could not create netstack NIC: %v", tcpipProblem)
@@ -256,10 +305,8 @@ func (ns *Impl) Start(lb *ipnlocal.LocalBackend) error {
256305
ns.lb = lb
257306
}
258307
ns.e.AddNetworkMapCallback(ns.updateIPs)
259-
// size = 0 means use default buffer size
260-
const tcpReceiveBufferSize = 0
261308
const maxInFlightConnectionAttempts = 1024
262-
tcpFwd := tcp.NewForwarder(ns.ipstack, tcpReceiveBufferSize, maxInFlightConnectionAttempts, ns.acceptTCP)
309+
tcpFwd := tcp.NewForwarder(ns.ipstack, recvBufSize, maxInFlightConnectionAttempts, ns.acceptTCP)
263310
udpFwd := udp.NewForwarder(ns.ipstack, ns.acceptUDP)
264311
ns.ipstack.SetTransportProtocolHandler(tcp.ProtocolNumber, ns.wrapProtoHandler(tcpFwd.HandlePacket))
265312
ns.ipstack.SetTransportProtocolHandler(udp.ProtocolNumber, ns.wrapProtoHandler(udpFwd.HandlePacket))

0 commit comments

Comments
 (0)