chore(pmtud): remove calls to syscall in favor of unix and windows

- syscall is deprecated and is not kept up-to-date
- each OS is inherently different hence the syscall being deprecated
This commit is contained in:
Quentin McGaw
2026-02-17 16:19:45 +00:00
parent d43eb1658f
commit 5f903d1fbf
21 changed files with 246 additions and 110 deletions
+3 -3
View File
@@ -5,9 +5,9 @@ import (
"errors"
"fmt"
"net/netip"
"syscall"
"time"
"github.com/qdm12/gluetun/internal/pmtud/constants"
"github.com/qdm12/gluetun/internal/pmtud/test"
)
@@ -32,9 +32,9 @@ func PathMTUDiscover(ctx context.Context, addrPort netip.AddrPort,
tests[i] = testUnit{mtu: mtusToTest[i]}
}
family := syscall.AF_INET
family := constants.AF_INET
if addrPort.Addr().Is6() {
family = syscall.AF_INET6
family = constants.AF_INET6
}
fd, stop, err := startRawSocket(family)
if err != nil {
+1 -2
View File
@@ -4,7 +4,6 @@ import (
"encoding/binary"
"math/rand/v2"
"net/netip"
"syscall"
"github.com/qdm12/gluetun/internal/pmtud/constants"
"github.com/qdm12/gluetun/internal/pmtud/ip"
@@ -67,7 +66,7 @@ func createPacket(src, dst netip.AddrPort,
ipHeader = ip.HeaderV4(src.Addr(), dst.Addr(), payloadLength)
} else {
ipHeader = ip.HeaderV6(src.Addr(), dst.Addr(),
uint16(payloadLength), byte(syscall.IPPROTO_TCP)) //nolint:gosec
uint16(payloadLength), byte(constants.IPPROTO_TCP)) //nolint:gosec
}
tcpHeader := makeTCPHeader(src.Port(), dst.Port(), seq, ack, flags)
+7 -21
View File
@@ -5,32 +5,31 @@ import (
"errors"
"fmt"
"net/netip"
"syscall"
"github.com/qdm12/gluetun/internal/pmtud/constants"
"github.com/qdm12/gluetun/internal/pmtud/ip"
)
func startRawSocket(family int) (fd fileDescriptor, stop func(), err error) {
fdPlatform, err := syscall.Socket(family, syscall.SOCK_RAW, syscall.IPPROTO_TCP)
fdPlatform, err := socket(family, constants.SOCK_RAW, constants.IPPROTO_TCP)
if err != nil {
return 0, nil, fmt.Errorf("creating raw socket: %w", err)
}
if family == syscall.AF_INET {
if family == constants.AF_INET {
err = ip.SetIPv4HeaderIncluded(fdPlatform)
} else {
err = ip.SetIPv6HeaderIncluded(fdPlatform)
}
if err != nil {
_ = syscall.Close(fdPlatform)
_ = closeSocket(fdPlatform)
return 0, nil, fmt.Errorf("setting header option on raw socket: %w", err)
}
// Allow sending packets larger than cached PMTU (for PMTUD probing)
err = setMTUDiscovery(fdPlatform)
if err != nil {
_ = syscall.Close(fdPlatform)
_ = closeSocket(fdPlatform)
return 0, nil, fmt.Errorf("setting IP_MTU_DISCOVER: %w", err)
}
@@ -39,12 +38,12 @@ func startRawSocket(family int) (fd fileDescriptor, stop func(), err error) {
// which would cause things to hang indefinitely.
err = setNonBlock(fdPlatform)
if err != nil {
_ = syscall.Close(fdPlatform)
_ = closeSocket(fdPlatform)
return 0, nil, fmt.Errorf("setting non-blocking mode: %w", err)
}
stop = func() {
_ = syscall.Close(fdPlatform)
_ = closeSocket(fdPlatform)
}
return fileDescriptor(fdPlatform), stop, nil
}
@@ -61,7 +60,7 @@ var (
func runTest(ctx context.Context, fd fileDescriptor,
tracker *tracker, dst netip.AddrPort, mtu uint32,
) error {
const proto = syscall.IPPROTO_TCP
const proto = constants.IPPROTO_TCP
src, cleanup, err := ip.SrcAddr(dst, proto)
if err != nil {
return fmt.Errorf("getting source address: %w", err)
@@ -137,19 +136,6 @@ func runTest(ctx context.Context, fd fileDescriptor,
}
}
func makeSockAddr(addr netip.AddrPort) syscall.Sockaddr {
if addr.Addr().Is4() {
return &syscall.SockaddrInet4{
Port: int(addr.Port()),
Addr: addr.Addr().As4(),
}
}
return &syscall.SockaddrInet6{
Port: int(addr.Port()),
Addr: addr.Addr().As16(),
}
}
var errTCPPacketNotRST = errors.New("TCP packet is not an RST")
func handleRSTReply(ctx context.Context, fd fileDescriptor, ch <-chan []byte,
+2 -2
View File
@@ -1,7 +1,7 @@
package tcp
import "syscall"
import "golang.org/x/sys/unix"
func setMTUDiscovery(fd int) error {
return syscall.SetsockoptInt(fd, syscall.IPPROTO_IP, syscall.IP_MTU_DISCOVER, syscall.IP_PMTUDISC_PROBE)
return unix.SetsockoptInt(fd, unix.IPPROTO_IP, unix.IP_MTU_DISCOVER, unix.IP_PMTUDISC_PROBE)
}
+14 -11
View File
@@ -1,3 +1,5 @@
//go:build linux
package tcp
import (
@@ -5,15 +7,16 @@ import (
"errors"
"fmt"
"net/netip"
"syscall"
"testing"
"time"
"github.com/qdm12/gluetun/internal/netlink"
"github.com/qdm12/gluetun/internal/pmtud/constants"
"github.com/qdm12/gluetun/internal/routing"
"github.com/qdm12/log"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"golang.org/x/sys/unix"
)
func Test_runTest(t *testing.T) {
@@ -30,7 +33,7 @@ func Test_runTest(t *testing.T) {
ctx, cancel := context.WithCancel(t.Context())
const family = syscall.AF_INET
const family = constants.AF_INET
fd, stop, err := startRawSocket(family)
require.NoError(t, err)
@@ -158,33 +161,33 @@ func findDefaultIPv4RouteMTU(netlinker *netlink.NetLink) (mtu uint32, err error)
func reserveClosedPort(t *testing.T) (port uint16) {
t.Helper()
fd, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_STREAM, syscall.IPPROTO_TCP)
fd, err := unix.Socket(constants.AF_INET, constants.SOCK_STREAM, constants.IPPROTO_TCP)
require.NoError(t, err)
t.Cleanup(func() {
err := syscall.Close(fd)
err := unix.Close(fd)
assert.NoError(t, err)
})
addr := &syscall.SockaddrInet4{
addr := &unix.SockaddrInet4{
Port: 0,
Addr: [4]byte{127, 0, 0, 1},
}
err = syscall.Bind(fd, addr)
err = unix.Bind(fd, addr)
if err != nil {
_ = syscall.Close(fd)
_ = unix.Close(fd)
t.Fatal(err)
}
sockAddr, err := syscall.Getsockname(fd)
sockAddr, err := unix.Getsockname(fd)
if err != nil {
_ = syscall.Close(fd)
_ = unix.Close(fd)
t.Fatal(err)
}
sockAddr4, ok := sockAddr.(*syscall.SockaddrInet4)
sockAddr4, ok := sockAddr.(*unix.SockaddrInet4)
if !ok {
_ = syscall.Close(fd)
_ = unix.Close(fd)
t.Fatal("not an IPv4 address")
}
+31 -8
View File
@@ -3,26 +3,49 @@
package tcp
import (
"syscall"
"net/netip"
"time"
"golang.org/x/sys/unix"
)
// fileDescriptor is a platform-independent type for socket file descriptors.
type fileDescriptor int
func sendTo(fd fileDescriptor, p []byte, flags int, to syscall.Sockaddr) (err error) {
return syscall.Sendto(int(fd), p, flags, to)
func socket(domain int, typ int, proto int) (fd int, err error) {
return unix.Socket(domain, typ, proto)
}
func closeSocket(fd int) error {
return unix.Close(fd)
}
func sendTo(fd fileDescriptor, p []byte, flags int, to unix.Sockaddr) (err error) {
return unix.Sendto(int(fd), p, flags, to)
}
func setSocketTimeout(fd fileDescriptor, timeout time.Duration) (err error) {
timeval := syscall.NsecToTimeval(timeout.Nanoseconds())
return syscall.SetsockoptTimeval(int(fd), syscall.SOL_SOCKET, syscall.SO_RCVTIMEO, &timeval)
timeval := unix.NsecToTimeval(timeout.Nanoseconds())
return unix.SetsockoptTimeval(int(fd), unix.SOL_SOCKET, unix.SO_RCVTIMEO, &timeval)
}
func recvFrom(fd fileDescriptor, p []byte, flags int) (n int, from syscall.Sockaddr, err error) {
return syscall.Recvfrom(int(fd), p, flags)
func recvFrom(fd fileDescriptor, p []byte, flags int) (n int, from unix.Sockaddr, err error) {
return unix.Recvfrom(int(fd), p, flags)
}
func setNonBlock(fd int) error {
return syscall.SetNonblock(fd, true)
return unix.SetNonblock(fd, true)
}
func makeSockAddr(addr netip.AddrPort) unix.Sockaddr {
if addr.Addr().Is4() {
return &unix.SockaddrInet4{
Port: int(addr.Port()),
Addr: addr.Addr().As4(),
}
}
return &unix.SockaddrInet6{
Port: int(addr.Port()),
Addr: addr.Addr().As16(),
}
}
+31 -10
View File
@@ -1,37 +1,58 @@
package tcp
import (
"syscall"
"net/netip"
"time"
"unsafe"
"golang.org/x/sys/windows"
)
type fileDescriptor syscall.Handle
type fileDescriptor windows.Handle
func sendTo(fd fileDescriptor, p []byte, flags int, to syscall.Sockaddr) (err error) {
return syscall.Sendto(syscall.Handle(fd), p, flags, to)
func socket(domain int, typ int, proto int) (fd windows.Handle, err error) {
return windows.Socket(domain, typ, proto)
}
func closeSocket(fd windows.Handle) error {
return windows.Close(fd)
}
func sendTo(fd fileDescriptor, p []byte, flags int, to windows.Sockaddr) (err error) {
return windows.Sendto(windows.Handle(fd), p, flags, to)
}
func setSocketTimeout(fd fileDescriptor, timeout time.Duration) (err error) {
timeval := int(timeout.Milliseconds())
return syscall.SetsockoptInt(syscall.Handle(fd), syscall.SOL_SOCKET, windows.SO_RCVTIMEO, timeval)
return windows.SetsockoptInt(windows.Handle(fd), windows.SOL_SOCKET, windows.SO_RCVTIMEO, timeval)
}
func recvFrom(fd fileDescriptor, p []byte, flags int) (n int, from syscall.Sockaddr, err error) {
return syscall.Recvfrom(syscall.Handle(fd), p, flags)
func recvFrom(fd fileDescriptor, p []byte, flags int) (n int, from windows.Sockaddr, err error) {
return windows.Recvfrom(windows.Handle(fd), p, flags)
}
func setMTUDiscovery(fd syscall.Handle) error {
func setMTUDiscovery(fd windows.Handle) error {
panic("not implemented")
}
func setNonBlock(fd syscall.Handle) error {
func setNonBlock(fd windows.Handle) error {
// Windows: Use ioctlsocket with FIONBIO
var arg uint32 = 1 // 1 to enable non-blocking mode
var bytesReturned uint32
const FIONBIO = 0x8004667e
return syscall.WSAIoctl(fd, FIONBIO, (*byte)(unsafe.Pointer(&arg)),
return windows.WSAIoctl(fd, FIONBIO, (*byte)(unsafe.Pointer(&arg)),
uint32(unsafe.Sizeof(arg)), nil, 0, &bytesReturned, nil, 0)
}
func makeSockAddr(addr netip.AddrPort) windows.Sockaddr {
if addr.Addr().Is4() {
return &windows.SockaddrInet4{
Port: int(addr.Port()),
Addr: addr.Addr().As4(),
}
}
return &windows.SockaddrInet6{
Port: int(addr.Port()),
Addr: addr.Addr().As16(),
}
}
+1 -3
View File
@@ -6,7 +6,6 @@ import (
"errors"
"fmt"
"sync"
"syscall"
"time"
"github.com/qdm12/gluetun/internal/pmtud/constants"
@@ -80,8 +79,7 @@ func (t *tracker) listen(ctx context.Context) error {
n, _, err := recvFrom(t.fd, reply, 0)
if err != nil {
switch {
case errors.Is(err, syscall.EAGAIN),
errors.Is(err, syscall.EWOULDBLOCK):
case errors.Is(err, constants.EAGAIN), errors.Is(err, constants.EWOULDBLOCK):
pollSleep(ctx)
continue
case ctx.Err() != nil: