From aeed2bac0755cc595ffb6f7d248f4c8c2a61b8e7 Mon Sep 17 00:00:00 2001 From: Jordan Whited Date: Thu, 2 Mar 2023 15:25:19 -0800 Subject: [PATCH] conn: set SO_{SND,RCV}BUF to 7MB on the Bind UDP socket The conn.Bind UDP socket send and receive buffers are now being sized to 7MB, whereas they were previously inheriting the system defaults. The value of 7MB is chosen as it is the max supported by a default configuration of macOS. Some platforms will silently clamp the value to other maximums. On Linux, we use SO_{SND,RCV}BUFFORCE in case 7MB is beyond net.core.{r,w}mem_max. Signed-off-by: Jordan Whited Signed-off-by: James Tucker Co-authored-by: James Tucker --- conn/controlfns.go | 7 +++++++ conn/controlfns_linux.go | 15 +++++++++++++++ conn/controlfns_unix.go | 9 +++++++++ conn/controlfns_windows.go | 21 +++++++++++++++++++++ 4 files changed, 52 insertions(+) create mode 100644 conn/controlfns_windows.go diff --git a/conn/controlfns.go b/conn/controlfns.go index fe32871a8..4f7d90fa1 100644 --- a/conn/controlfns.go +++ b/conn/controlfns.go @@ -10,6 +10,13 @@ import ( "syscall" ) +// UDP socket read/write buffer size (7MB). The value of 7MB is chosen as it is +// the max supported by a default configuration of macOS. Some platforms will +// silently clamp the value to other maximums, such as linux clamping to +// net.core.{r,w}mem_max (see _linux.go for additional implementation that works +// around this limitation) +const socketBufferSize = 7 << 20 + // controlFn is the callback function signature from net.ListenConfig.Control. // It is used to apply platform specific configuration to the socket prior to // bind. diff --git a/conn/controlfns_linux.go b/conn/controlfns_linux.go index 9e26d9527..aff62456f 100644 --- a/conn/controlfns_linux.go +++ b/conn/controlfns_linux.go @@ -15,6 +15,21 @@ import ( func init() { controlFns = append(controlFns, + // Attempt to set the socket buffer size beyond net.core.{r,w}mem_max by + // using SO_*BUFFORCE. This requires CAP_NET_ADMIN, and is allowed here to + // fail silently - the result of failure is lower performance on very fast + // links or high latency links. + func(network, address string, c syscall.RawConn) error { + return c.Control(func(fd uintptr) { + // Set up to *mem_max + _ = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_RCVBUF, socketBufferSize) + _ = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_SNDBUF, socketBufferSize) + // Set beyond *mem_max if CAP_NET_ADMIN + _ = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_RCVBUFFORCE, socketBufferSize) + _ = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_SNDBUFFORCE, socketBufferSize) + }) + }, + // Enable receiving of the packet information (IP_PKTINFO for IPv4, // IPV6_PKTINFO for IPv6) that is used to implement sticky socket support. func(network, address string, c syscall.RawConn) error { diff --git a/conn/controlfns_unix.go b/conn/controlfns_unix.go index 9738c73d3..726ee5467 100644 --- a/conn/controlfns_unix.go +++ b/conn/controlfns_unix.go @@ -15,6 +15,15 @@ import ( func init() { controlFns = append(controlFns, + // Set SO_RCVBUF/SO_SNDBUF - this could be common with the _windows code except + // for the unfortunate type specificity of syscall.Handle. + func(network, address string, c syscall.RawConn) error { + return c.Control(func(fd uintptr) { + _ = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_RCVBUF, socketBufferSize) + _ = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_SNDBUF, socketBufferSize) + }) + }, + func(network, address string, c syscall.RawConn) error { var err error if network == "udp6" { diff --git a/conn/controlfns_windows.go b/conn/controlfns_windows.go new file mode 100644 index 000000000..46cf03d87 --- /dev/null +++ b/conn/controlfns_windows.go @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: MIT + * + * Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved. + */ + +package conn + +import "syscall" + +func init() { + controlFns = append(controlFns, + // Set SO_RCVBUF/SO_SNDBUF - this could be common with the _unix code except + // for the unfortunate type specificity of syscall.Handle. + func(network, address string, c syscall.RawConn) error { + return c.Control(func(fd uintptr) { + _ = syscall.SetsockoptInt(syscall.Handle(fd), syscall.SOL_SOCKET, syscall.SO_RCVBUF, socketBufferSize) + _ = syscall.SetsockoptInt(syscall.Handle(fd), syscall.SOL_SOCKET, syscall.SO_SNDBUF, socketBufferSize) + }) + }, + ) +}