Skip to content

Commit

Permalink
📡 conn: add option for probing UDP GSO support
Browse files Browse the repository at this point in the history
  • Loading branch information
database64128 committed Sep 30, 2024
1 parent 0f8fcd9 commit be2b06f
Show file tree
Hide file tree
Showing 4 changed files with 37 additions and 0 deletions.
6 changes: 6 additions & 0 deletions conn/conn.go
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,12 @@ type ListenerSocketOptions struct {
// Available on platforms supported by Go std's MPTCP implementation.
MultipathTCP bool

// ProbeUDPGSOSupport enables best-effort probing of
// UDP Generic Segmentation Offload (GSO) support on the listener.
//
// Available on Linux and Windows.
ProbeUDPGSOSupport bool

// UDPGenericReceiveOffload enables UDP Generic Receive Offload (GRO) on the listener.
//
// Available on Linux and Windows.
Expand Down
10 changes: 10 additions & 0 deletions conn/conn_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,15 @@ func setTCPUserTimeout(fd, msecs int) error {
return nil
}

func probeUDPGSOSupport(fd int, info *SocketInfo) {
if err := unix.SetsockoptInt(fd, unix.IPPROTO_UDP, unix.UDP_SEGMENT, 0); err == nil {
// UDP_MAX_SEGMENTS as defined in linux/udp.h was originally 64.
// It got bumped to 128 in Linux 6.9: https://github.com/torvalds/linux/commit/1382e3b6a3500c245e5278c66d210c02926f804f
// The receive path still only supports 64 segments, so 64 it is.
info.MaxUDPGSOSegments = 64
}
}

func setUDPGenericReceiveOffload(fd int, info *SocketInfo) {
if err := unix.SetsockoptInt(fd, unix.IPPROTO_UDP, unix.UDP_GRO, 1); err == nil {
info.UDPGenericReceiveOffload = true
Expand Down Expand Up @@ -190,6 +199,7 @@ func (lso ListenerSocketOptions) buildSetFns() setFuncSlice {
appendSetReusePortFunc(lso.ReusePort).
appendSetTransparentFunc(lso.Transparent).
appendSetPMTUDFunc(lso.PathMTUDiscovery).
appendProbeUDPGSOSupportFunc(lso.ProbeUDPGSOSupport).
appendSetUDPGenericReceiveOffloadFunc(lso.UDPGenericReceiveOffload).
appendSetRecvPktinfoFunc(lso.ReceivePacketInfo).
appendSetRecvOrigDstAddrFunc(lso.ReceiveOriginalDestAddr)
Expand Down
10 changes: 10 additions & 0 deletions conn/conn_udpgro.go → conn/conn_udpgsogro.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,16 @@

package conn

func (fns setFuncSlice) appendProbeUDPGSOSupportFunc(probeUDPGSO bool) setFuncSlice {
if probeUDPGSO {
return append(fns, func(fd int, _ string, info *SocketInfo) error {
probeUDPGSOSupport(fd, info)
return nil
})
}
return fns
}

func (fns setFuncSlice) appendSetUDPGenericReceiveOffloadFunc(gro bool) setFuncSlice {
if gro {
return append(fns, func(fd int, _ string, info *SocketInfo) error {
Expand Down
11 changes: 11 additions & 0 deletions conn/conn_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,16 @@ func setPMTUD(fd int, network string) error {
return nil
}

// Implementation inspired by:
// https://github.com/quinn-rs/quinn/blob/main/quinn-udp/src/windows.rs

func probeUDPGSOSupport(fd int, info *SocketInfo) {
if err := windows.SetsockoptInt(windows.Handle(fd), windows.IPPROTO_UDP, windows.UDP_SEND_MSG_SIZE, 0); err == nil {
// As "empirically found on Windows 11 x64" by quinn.
info.MaxUDPGSOSegments = 512
}
}

func setUDPGenericReceiveOffload(fd int, info *SocketInfo) {
// Both quinn and msquic set this to 65535.
if err := windows.SetsockoptInt(windows.Handle(fd), windows.IPPROTO_UDP, windows.UDP_RECV_MAX_COALESCED_SIZE, 65535); err == nil {
Expand Down Expand Up @@ -84,6 +94,7 @@ func (lso ListenerSocketOptions) buildSetFns() setFuncSlice {
appendSetSendBufferSize(lso.SendBufferSize).
appendSetRecvBufferSize(lso.ReceiveBufferSize).
appendSetPMTUDFunc(lso.PathMTUDiscovery).
appendProbeUDPGSOSupportFunc(lso.ProbeUDPGSOSupport).
appendSetUDPGenericReceiveOffloadFunc(lso.UDPGenericReceiveOffload).
appendSetRecvPktinfoFunc(lso.ReceivePacketInfo)
}

0 comments on commit be2b06f

Please sign in to comment.