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
+2 -3
View File
@@ -3,7 +3,6 @@ package ip
import (
"encoding/binary"
"net/netip"
"syscall"
"github.com/qdm12/gluetun/internal/pmtud/constants"
)
@@ -19,7 +18,7 @@ func HeaderV4(srcIP, dstIP netip.Addr, payloadLength uint32) []byte {
const flagsAndOffset uint16 = 0x4000 // DF bit set
putUint16(ipHeader[6:], flagsAndOffset)
ipHeader[8] = 64 // ttl
ipHeader[9] = syscall.IPPROTO_TCP
ipHeader[9] = constants.IPPROTO_TCP
srcIPBytes := srcIP.As4()
copy(ipHeader[12:16], srcIPBytes[:])
dstIPBytes := dstIP.As4()
@@ -51,7 +50,7 @@ func ipChecksum(header []byte) uint16 {
// HeaderV6 makes an IPv6 header.
// payloadLen is the length of the payload following the header.
// nextHeader can be byte([syscall.IPPROTO_TCP]) for example.
// nextHeader can be byte([constants.IPPROTO_TCP]) for example.
func HeaderV6(srcIP, dstIP netip.Addr,
payloadLen uint16, nextHeader byte,
) []byte {
+2 -2
View File
@@ -2,8 +2,8 @@
package ip
import "syscall"
import "golang.org/x/sys/unix"
func SetIPv4HeaderIncluded(fd int) error {
return syscall.SetsockoptInt(fd, syscall.IPPROTO_IP, syscall.IP_HDRINCL, 1)
return unix.SetsockoptInt(fd, unix.IPPROTO_IP, unix.IP_HDRINCL, 1)
}
+2 -4
View File
@@ -1,12 +1,10 @@
package ip
import (
"syscall"
"golang.org/x/sys/windows"
)
func SetIPv4HeaderIncluded(handle syscall.Handle) error {
func SetIPv4HeaderIncluded(handle windows.Handle) error {
const ipHdrIncluded = windows.IP_HDRINCL
return syscall.SetsockoptInt(handle, syscall.IPPROTO_IP, ipHdrIncluded, 1)
return windows.SetsockoptInt(handle, windows.IPPROTO_IP, ipHdrIncluded, 1)
}
+2 -3
View File
@@ -1,8 +1,7 @@
package ip
import "syscall"
import "golang.org/x/sys/unix"
func SetIPv6HeaderIncluded(fd int) error {
const ipv6HdrIncluded = 36 // IPV6_HDRINCL
return syscall.SetsockoptInt(fd, syscall.IPPROTO_IPV6, ipv6HdrIncluded, 1)
return unix.SetsockoptInt(fd, unix.IPPROTO_IPV6, unix.IPV6_HDRINCL, 1)
}
+2 -2
View File
@@ -1,7 +1,7 @@
package ip
import "syscall"
import "golang.org/x/sys/windows"
func SetIPv6HeaderIncluded(fd syscall.Handle) error {
func SetIPv6HeaderIncluded(fd windows.Handle) error {
panic("windows does not allow an application to build IPv6 headers")
}
+11 -30
View File
@@ -3,9 +3,9 @@ package ip
import (
"fmt"
"net/netip"
"syscall"
"github.com/jsimonetti/rtnetlink"
"github.com/qdm12/gluetun/internal/pmtud/constants"
)
// SrcAddr determines the appropriate source IP address to use when sending a packet to the
@@ -35,9 +35,9 @@ func srcIP(dst netip.Addr) (netip.Addr, error) {
}
defer conn.Close()
family := uint8(syscall.AF_INET)
family := uint8(constants.AF_INET)
if dst.Is6() {
family = syscall.AF_INET6
family = constants.AF_INET6
}
// Request route to destination
@@ -71,53 +71,34 @@ func srcIP(dst netip.Addr) (netip.Addr, error) {
// It doesn't actually listen on the port.
// The cleanup function returned should be called to release the port when done.
func srcPort(srcAddr netip.Addr, proto int) (srcPort uint16, cleanup func(), err error) {
family := syscall.AF_INET
family := constants.AF_INET
if srcAddr.Is6() {
family = syscall.AF_INET6
family = constants.AF_INET6
}
fd, err := syscall.Socket(family, syscall.SOCK_STREAM, proto)
fd, err := socket(family, constants.SOCK_STREAM, proto)
if err != nil {
return 0, nil, fmt.Errorf("creating reservation socket: %w", err)
}
cleanup = func() {
_ = syscall.Close(fd)
_ = closeSocket(fd)
}
// Bind to port 0 to get an ephemeral port
const port = 0
var bindAddr syscall.Sockaddr
if srcAddr.Is4() {
bindAddr = &syscall.SockaddrInet4{
Port: port,
Addr: srcAddr.As4(),
}
} else {
bindAddr = &syscall.SockaddrInet6{
Port: port,
Addr: srcAddr.As16(),
}
}
bindAddr := makeSockAddr(srcAddr, port)
err = syscall.Bind(fd, bindAddr)
err = bind(fd, bindAddr)
if err != nil {
cleanup()
return 0, nil, fmt.Errorf("binding reservation socket: %w", err)
}
sockAddr, err := syscall.Getsockname(fd)
srcPort, err = extractPortFromFD(fd)
if err != nil {
cleanup()
return 0, nil, fmt.Errorf("getting bound socket name: %w", err)
return 0, nil, fmt.Errorf("extracting port from socket fd: %w", err)
}
switch typedSockAddr := sockAddr.(type) {
case *syscall.SockaddrInet4:
srcPort = uint16(typedSockAddr.Port) //nolint:gosec
case *syscall.SockaddrInet6:
srcPort = uint16(typedSockAddr.Port) //nolint:gosec
default:
panic(fmt.Sprintf("unexpected sockaddr type: %T", typedSockAddr))
}
return srcPort, cleanup, nil
}
+51
View File
@@ -0,0 +1,51 @@
//go:build linux || darwin
package ip
import (
"fmt"
"net/netip"
"golang.org/x/sys/unix"
)
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 bind(fd int, addr unix.Sockaddr) error {
return unix.Bind(fd, addr)
}
func makeSockAddr(ip netip.Addr, port uint16) unix.Sockaddr {
if ip.Is4() {
return &unix.SockaddrInet4{
Port: int(port),
Addr: ip.As4(),
}
}
return &unix.SockaddrInet6{
Port: int(port),
Addr: ip.As16(),
}
}
func extractPortFromFD(fd int) (uint16, error) {
sockAddr, err := unix.Getsockname(fd)
if err != nil {
return 0, fmt.Errorf("getting sockname: %w", err)
}
switch typedSockAddr := sockAddr.(type) {
case *unix.SockaddrInet4:
return uint16(typedSockAddr.Port), nil //nolint:gosec
case *unix.SockaddrInet6:
return uint16(typedSockAddr.Port), nil //nolint:gosec
default:
panic(fmt.Sprintf("unexpected sockaddr type: %T", typedSockAddr))
}
}
+49
View File
@@ -0,0 +1,49 @@
package ip
import (
"fmt"
"net/netip"
"golang.org/x/sys/windows"
)
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 bind(fd windows.Handle, addr windows.Sockaddr) error {
return windows.Bind(fd, addr)
}
func makeSockAddr(ip netip.Addr, port uint16) windows.Sockaddr {
if ip.Is4() {
return &windows.SockaddrInet4{
Port: int(port),
Addr: ip.As4(),
}
}
return &windows.SockaddrInet6{
Port: int(port),
Addr: ip.As16(),
}
}
func extractPortFromFD(fd windows.Handle) (uint16, error) {
sockAddr, err := windows.Getsockname(fd)
if err != nil {
return 0, fmt.Errorf("getting sockname: %w", err)
}
switch typedSockAddr := sockAddr.(type) {
case *windows.SockaddrInet4:
return uint16(typedSockAddr.Port), nil //nolint:gosec
case *windows.SockaddrInet6:
return uint16(typedSockAddr.Port), nil //nolint:gosec
default:
panic(fmt.Sprintf("unexpected sockaddr type: %T", typedSockAddr))
}
}