aboutsummaryrefslogtreecommitdiffstats
path: root/conn/controlfns_linux.go
diff options
context:
space:
mode:
Diffstat (limited to 'conn/controlfns_linux.go')
-rw-r--r--conn/controlfns_linux.go109
1 files changed, 109 insertions, 0 deletions
diff --git a/conn/controlfns_linux.go b/conn/controlfns_linux.go
new file mode 100644
index 0000000..f0deefa
--- /dev/null
+++ b/conn/controlfns_linux.go
@@ -0,0 +1,109 @@
+/* SPDX-License-Identifier: MIT
+ *
+ * Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
+ */
+
+package conn
+
+import (
+ "fmt"
+ "runtime"
+ "syscall"
+
+ "golang.org/x/sys/unix"
+)
+
+// Taken from go/src/internal/syscall/unix/kernel_version_linux.go
+func kernelVersion() (major, minor int) {
+ var uname unix.Utsname
+ if err := unix.Uname(&uname); err != nil {
+ return
+ }
+
+ var (
+ values [2]int
+ value, vi int
+ )
+ for _, c := range uname.Release {
+ if '0' <= c && c <= '9' {
+ value = (value * 10) + int(c-'0')
+ } else {
+ // Note that we're assuming N.N.N here.
+ // If we see anything else, we are likely to mis-parse it.
+ values[vi] = value
+ vi++
+ if vi >= len(values) {
+ break
+ }
+ value = 0
+ }
+ }
+
+ return values[0], values[1]
+}
+
+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 {
+ var err error
+ switch network {
+ case "udp4":
+ if runtime.GOOS != "android" {
+ c.Control(func(fd uintptr) {
+ err = unix.SetsockoptInt(int(fd), unix.IPPROTO_IP, unix.IP_PKTINFO, 1)
+ })
+ }
+ case "udp6":
+ c.Control(func(fd uintptr) {
+ if runtime.GOOS != "android" {
+ err = unix.SetsockoptInt(int(fd), unix.IPPROTO_IPV6, unix.IPV6_RECVPKTINFO, 1)
+ if err != nil {
+ return
+ }
+ }
+ err = unix.SetsockoptInt(int(fd), unix.IPPROTO_IPV6, unix.IPV6_V6ONLY, 1)
+ })
+ default:
+ err = fmt.Errorf("unhandled network: %s: %w", network, unix.EINVAL)
+ }
+ return err
+ },
+
+ // Attempt to enable UDP_GRO
+ func(network, address string, c syscall.RawConn) error {
+ // Kernels below 5.12 are missing 98184612aca0 ("net:
+ // udp: Add support for getsockopt(..., ..., UDP_GRO,
+ // ..., ...);"), which means we can't read this back
+ // later. We could pipe the return value through to
+ // the rest of the code, but UDP_GRO is kind of buggy
+ // anyway, so just gate this here.
+ major, minor := kernelVersion()
+ if major < 5 || (major == 5 && minor < 12) {
+ return nil
+ }
+
+ c.Control(func(fd uintptr) {
+ _ = unix.SetsockoptInt(int(fd), unix.IPPROTO_UDP, unix.UDP_GRO, 1)
+ })
+ return nil
+ },
+ )
+}