conn: separate gso and sticky control

Android wants GSO but not sticky.

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
This commit is contained in:
Jason A. Donenfeld 2023-10-21 18:41:27 +02:00
parent 24ea13351e
commit 5d37bd24e1
6 changed files with 96 additions and 66 deletions

View File

@ -65,7 +65,7 @@ func NewStdNetBind() Bind {
msgs := make([]ipv6.Message, IdealBatchSize) msgs := make([]ipv6.Message, IdealBatchSize)
for i := range msgs { for i := range msgs {
msgs[i].Buffers = make(net.Buffers, 1) msgs[i].Buffers = make(net.Buffers, 1)
msgs[i].OOB = make([]byte, controlSize) msgs[i].OOB = make([]byte, stickyControlSize+gsoControlSize)
} }
return &msgs return &msgs
}, },

21
conn/gso_default.go Normal file
View File

@ -0,0 +1,21 @@
//go:build !linux
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
*/
package conn
// getGSOSize parses control for UDP_GRO and if found returns its GSO size data.
func getGSOSize(control []byte) (int, error) {
return 0, nil
}
// setGSOSize sets a UDP_SEGMENT in control based on gsoSize.
func setGSOSize(control *[]byte, gsoSize uint16) {
}
// gsoControlSize returns the recommended buffer size for pooling sticky and UDP
// offloading control data.
const gsoControlSize = 0

65
conn/gso_linux.go Normal file
View File

@ -0,0 +1,65 @@
//go:build linux
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2017-2023 WireGuard LLC. All Rights Reserved.
*/
package conn
import (
"fmt"
"unsafe"
"golang.org/x/sys/unix"
)
const (
sizeOfGSOData = 2
)
// getGSOSize parses control for UDP_GRO and if found returns its GSO size data.
func getGSOSize(control []byte) (int, error) {
var (
hdr unix.Cmsghdr
data []byte
rem = control
err error
)
for len(rem) > unix.SizeofCmsghdr {
hdr, data, rem, err = unix.ParseOneSocketControlMessage(rem)
if err != nil {
return 0, fmt.Errorf("error parsing socket control message: %w", err)
}
if hdr.Level == unix.SOL_UDP && hdr.Type == unix.UDP_GRO && len(data) >= sizeOfGSOData {
var gso uint16
copy(unsafe.Slice((*byte)(unsafe.Pointer(&gso)), sizeOfGSOData), data[:sizeOfGSOData])
return int(gso), nil
}
}
return 0, nil
}
// setGSOSize sets a UDP_SEGMENT in control based on gsoSize. It leaves existing
// data in control untouched.
func setGSOSize(control *[]byte, gsoSize uint16) {
existingLen := len(*control)
avail := cap(*control) - existingLen
space := unix.CmsgSpace(sizeOfGSOData)
if avail < space {
return
}
*control = (*control)[:cap(*control)]
gsoControl := (*control)[existingLen:]
hdr := (*unix.Cmsghdr)(unsafe.Pointer(&(gsoControl)[0]))
hdr.Level = unix.SOL_UDP
hdr.Type = unix.UDP_SEGMENT
hdr.SetLen(unix.CmsgLen(sizeOfGSOData))
copy((gsoControl)[unix.SizeofCmsghdr:], unsafe.Slice((*byte)(unsafe.Pointer(&gsoSize)), sizeOfGSOData))
*control = (*control)[:existingLen+space]
}
// gsoControlSize returns the recommended buffer size for pooling UDP
// offloading control data.
var gsoControlSize = unix.CmsgSpace(sizeOfGSOData)

View File

@ -35,17 +35,8 @@ func getSrcFromControl(control []byte, ep *StdNetEndpoint) {
func setSrcControl(control *[]byte, ep *StdNetEndpoint) { func setSrcControl(control *[]byte, ep *StdNetEndpoint) {
} }
// getGSOSize parses control for UDP_GRO and if found returns its GSO size data. // stickyControlSize returns the recommended buffer size for pooling sticky
func getGSOSize(control []byte) (int, error) {
return 0, nil
}
// setGSOSize sets a UDP_SEGMENT in control based on gsoSize.
func setGSOSize(control *[]byte, gsoSize uint16) {
}
// controlSize returns the recommended buffer size for pooling sticky and UDP
// offloading control data. // offloading control data.
const controlSize = 0 const stickyControlSize = 0
const StdNetSupportsStickySockets = false const StdNetSupportsStickySockets = false

View File

@ -8,7 +8,6 @@
package conn package conn
import ( import (
"fmt"
"net/netip" "net/netip"
"unsafe" "unsafe"
@ -106,54 +105,8 @@ func setSrcControl(control *[]byte, ep *StdNetEndpoint) {
*control = append(*control, ep.src...) *control = append(*control, ep.src...)
} }
const ( // stickyControlSize returns the recommended buffer size for pooling sticky
sizeOfGSOData = 2
)
// getGSOSize parses control for UDP_GRO and if found returns its GSO size data.
func getGSOSize(control []byte) (int, error) {
var (
hdr unix.Cmsghdr
data []byte
rem = control
err error
)
for len(rem) > unix.SizeofCmsghdr {
hdr, data, rem, err = unix.ParseOneSocketControlMessage(rem)
if err != nil {
return 0, fmt.Errorf("error parsing socket control message: %w", err)
}
if hdr.Level == unix.SOL_UDP && hdr.Type == unix.UDP_GRO && len(data) >= sizeOfGSOData {
var gso uint16
copy(unsafe.Slice((*byte)(unsafe.Pointer(&gso)), sizeOfGSOData), data[:sizeOfGSOData])
return int(gso), nil
}
}
return 0, nil
}
// setGSOSize sets a UDP_SEGMENT in control based on gsoSize. It leaves existing
// data in control untouched.
func setGSOSize(control *[]byte, gsoSize uint16) {
existingLen := len(*control)
avail := cap(*control) - existingLen
space := unix.CmsgSpace(sizeOfGSOData)
if avail < space {
return
}
*control = (*control)[:cap(*control)]
gsoControl := (*control)[existingLen:]
hdr := (*unix.Cmsghdr)(unsafe.Pointer(&(gsoControl)[0]))
hdr.Level = unix.SOL_UDP
hdr.Type = unix.UDP_SEGMENT
hdr.SetLen(unix.CmsgLen(sizeOfGSOData))
copy((gsoControl)[unix.SizeofCmsghdr:], unsafe.Slice((*byte)(unsafe.Pointer(&gsoSize)), sizeOfGSOData))
*control = (*control)[:existingLen+space]
}
// controlSize returns the recommended buffer size for pooling sticky and UDP
// offloading control data. // offloading control data.
var controlSize = unix.CmsgSpace(unix.SizeofInet6Pktinfo) + unix.CmsgSpace(sizeOfGSOData) var stickyControlSize = unix.CmsgSpace(unix.SizeofInet6Pktinfo)
const StdNetSupportsStickySockets = true const StdNetSupportsStickySockets = true

View File

@ -60,7 +60,7 @@ func Test_setSrcControl(t *testing.T) {
} }
setSrc(ep, netip.MustParseAddr("127.0.0.1"), 5) setSrc(ep, netip.MustParseAddr("127.0.0.1"), 5)
control := make([]byte, controlSize) control := make([]byte, stickyControlSize)
setSrcControl(&control, ep) setSrcControl(&control, ep)
@ -89,7 +89,7 @@ func Test_setSrcControl(t *testing.T) {
} }
setSrc(ep, netip.MustParseAddr("::1"), 5) setSrc(ep, netip.MustParseAddr("::1"), 5)
control := make([]byte, controlSize) control := make([]byte, stickyControlSize)
setSrcControl(&control, ep) setSrcControl(&control, ep)
@ -113,7 +113,7 @@ func Test_setSrcControl(t *testing.T) {
}) })
t.Run("ClearOnNoSrc", func(t *testing.T) { t.Run("ClearOnNoSrc", func(t *testing.T) {
control := make([]byte, controlSize) control := make([]byte, stickyControlSize)
hdr := (*unix.Cmsghdr)(unsafe.Pointer(&control[0])) hdr := (*unix.Cmsghdr)(unsafe.Pointer(&control[0]))
hdr.Level = 1 hdr.Level = 1
hdr.Type = 2 hdr.Type = 2
@ -129,7 +129,7 @@ func Test_setSrcControl(t *testing.T) {
func Test_getSrcFromControl(t *testing.T) { func Test_getSrcFromControl(t *testing.T) {
t.Run("IPv4", func(t *testing.T) { t.Run("IPv4", func(t *testing.T) {
control := make([]byte, controlSize) control := make([]byte, stickyControlSize)
hdr := (*unix.Cmsghdr)(unsafe.Pointer(&control[0])) hdr := (*unix.Cmsghdr)(unsafe.Pointer(&control[0]))
hdr.Level = unix.IPPROTO_IP hdr.Level = unix.IPPROTO_IP
hdr.Type = unix.IP_PKTINFO hdr.Type = unix.IP_PKTINFO
@ -149,7 +149,7 @@ func Test_getSrcFromControl(t *testing.T) {
} }
}) })
t.Run("IPv6", func(t *testing.T) { t.Run("IPv6", func(t *testing.T) {
control := make([]byte, controlSize) control := make([]byte, stickyControlSize)
hdr := (*unix.Cmsghdr)(unsafe.Pointer(&control[0])) hdr := (*unix.Cmsghdr)(unsafe.Pointer(&control[0]))
hdr.Level = unix.IPPROTO_IPV6 hdr.Level = unix.IPPROTO_IPV6
hdr.Type = unix.IPV6_PKTINFO hdr.Type = unix.IPV6_PKTINFO