gso_linux.go raw
1 //go:build linux
2
3 /* SPDX-License-Identifier: MIT
4 *
5 * Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
6 */
7
8 package conn
9
10 import (
11 "fmt"
12 "unsafe"
13
14 "golang.org/x/sys/unix"
15 )
16
17 const (
18 sizeOfGSOData = 2
19 )
20
21 // getGSOSize parses control for UDP_GRO and if found returns its GSO size data.
22 func getGSOSize(control []byte) (int, error) {
23 var (
24 hdr unix.Cmsghdr
25 data []byte
26 rem = control
27 err error
28 )
29
30 for len(rem) > unix.SizeofCmsghdr {
31 hdr, data, rem, err = unix.ParseOneSocketControlMessage(rem)
32 if err != nil {
33 return 0, fmt.Errorf("error parsing socket control message: %w", err)
34 }
35 if hdr.Level == unix.SOL_UDP && hdr.Type == unix.UDP_GRO && len(data) >= sizeOfGSOData {
36 var gso uint16
37 copy(unsafe.Slice((*byte)(unsafe.Pointer(&gso)), sizeOfGSOData), data[:sizeOfGSOData])
38 return int(gso), nil
39 }
40 }
41 return 0, nil
42 }
43
44 // setGSOSize sets a UDP_SEGMENT in control based on gsoSize. It leaves existing
45 // data in control untouched.
46 func setGSOSize(control *[]byte, gsoSize uint16) {
47 existingLen := len(*control)
48 avail := cap(*control) - existingLen
49 space := unix.CmsgSpace(sizeOfGSOData)
50 if avail < space {
51 return
52 }
53 *control = (*control)[:cap(*control)]
54 gsoControl := (*control)[existingLen:]
55 hdr := (*unix.Cmsghdr)(unsafe.Pointer(&(gsoControl)[0]))
56 hdr.Level = unix.SOL_UDP
57 hdr.Type = unix.UDP_SEGMENT
58 hdr.SetLen(unix.CmsgLen(sizeOfGSOData))
59 copy((gsoControl)[unix.CmsgLen(0):], unsafe.Slice((*byte)(unsafe.Pointer(&gsoSize)), sizeOfGSOData))
60 *control = (*control)[:existingLen+space]
61 }
62
63 // gsoControlSize returns the recommended buffer size for pooling UDP
64 // offloading control data.
65 var gsoControlSize = unix.CmsgSpace(sizeOfGSOData)
66