controlfns_linux.go raw
1 /* SPDX-License-Identifier: MIT
2 *
3 * Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
4 */
5
6 package conn
7
8 import (
9 "fmt"
10 "runtime"
11 "syscall"
12
13 "golang.org/x/sys/unix"
14 )
15
16 // Taken from go/src/internal/syscall/unix/kernel_version_linux.go
17 func kernelVersion() (major, minor int) {
18 var uname unix.Utsname
19 if err := unix.Uname(&uname); err != nil {
20 return
21 }
22
23 var (
24 values [2]int
25 value, vi int
26 )
27 for _, c := range uname.Release {
28 if '0' <= c && c <= '9' {
29 value = (value * 10) + int(c-'0')
30 } else {
31 // Note that we're assuming N.N.N here.
32 // If we see anything else, we are likely to mis-parse it.
33 values[vi] = value
34 vi++
35 if vi >= len(values) {
36 break
37 }
38 value = 0
39 }
40 }
41
42 return values[0], values[1]
43 }
44
45 func init() {
46 controlFns = append(controlFns,
47
48 // Attempt to set the socket buffer size beyond net.core.{r,w}mem_max by
49 // using SO_*BUFFORCE. This requires CAP_NET_ADMIN, and is allowed here to
50 // fail silently - the result of failure is lower performance on very fast
51 // links or high latency links.
52 func(network, address string, c syscall.RawConn) error {
53 return c.Control(func(fd uintptr) {
54 // Set up to *mem_max
55 _ = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_RCVBUF, socketBufferSize)
56 _ = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_SNDBUF, socketBufferSize)
57 // Set beyond *mem_max if CAP_NET_ADMIN
58 _ = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_RCVBUFFORCE, socketBufferSize)
59 _ = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_SNDBUFFORCE, socketBufferSize)
60 })
61 },
62
63 // Enable receiving of the packet information (IP_PKTINFO for IPv4,
64 // IPV6_PKTINFO for IPv6) that is used to implement sticky socket support.
65 func(network, address string, c syscall.RawConn) error {
66 var err error
67 switch network {
68 case "udp4":
69 if runtime.GOOS != "android" {
70 c.Control(func(fd uintptr) {
71 err = unix.SetsockoptInt(int(fd), unix.IPPROTO_IP, unix.IP_PKTINFO, 1)
72 })
73 }
74 case "udp6":
75 c.Control(func(fd uintptr) {
76 if runtime.GOOS != "android" {
77 err = unix.SetsockoptInt(int(fd), unix.IPPROTO_IPV6, unix.IPV6_RECVPKTINFO, 1)
78 if err != nil {
79 return
80 }
81 }
82 err = unix.SetsockoptInt(int(fd), unix.IPPROTO_IPV6, unix.IPV6_V6ONLY, 1)
83 })
84 default:
85 err = fmt.Errorf("unhandled network: %s: %w", network, unix.EINVAL)
86 }
87 return err
88 },
89
90 // Attempt to enable UDP_GRO
91 func(network, address string, c syscall.RawConn) error {
92 // Kernels below 5.12 are missing 98184612aca0 ("net:
93 // udp: Add support for getsockopt(..., ..., UDP_GRO,
94 // ..., ...);"), which means we can't read this back
95 // later. We could pipe the return value through to
96 // the rest of the code, but UDP_GRO is kind of buggy
97 // anyway, so just gate this here.
98 major, minor := kernelVersion()
99 if major < 5 || (major == 5 && minor < 12) {
100 return nil
101 }
102
103 c.Control(func(fd uintptr) {
104 _ = unix.SetsockoptInt(int(fd), unix.IPPROTO_UDP, unix.UDP_GRO, 1)
105 })
106 return nil
107 },
108 )
109 }
110