Explorar o código

magicsock: set the don't fragment sockopt (#8715)

This sets the Don't Fragment flag, for now behind the
TS_DEBUG_ENABLE_PMTUD envknob.

Updates #311.

Signed-off-by: Val <[email protected]>
Signed-off-by: salman <[email protected]>
salman aljammaz %!s(int64=2) %!d(string=hai) anos
pai
achega
99e06d3544

+ 2 - 0
wgengine/magicsock/debugknobs.go

@@ -47,6 +47,8 @@ var (
 	// debugRingBufferMaxSizeBytes overrides the default size of the endpoint
 	// history ringbuffer.
 	debugRingBufferMaxSizeBytes = envknob.RegisterInt("TS_DEBUG_MAGICSOCK_RING_BUFFER_MAX_SIZE_BYTES")
+	// debugPMTUD enables path MTU discovery. Currently only sets the Don't Fragment sockopt.
+	debugPMTUD = envknob.RegisterBool("TS_DEBUG_ENABLE_PMTUD")
 	// Hey you! Adding a new debugknob? Make sure to stub it out in the debugknob_stubs.go
 	// file too.
 )

+ 1 - 0
wgengine/magicsock/debugknobs_stubs.go

@@ -20,6 +20,7 @@ func debugAlwaysDERP() bool            { return false }
 func debugUseDERPHTTP() bool           { return false }
 func debugEnableSilentDisco() bool     { return false }
 func debugSendCallMeUnknownPeer() bool { return false }
+func debugPMTUD() bool                 { return false }
 func debugUseDERPAddr() string         { return "" }
 func debugUseDerpRouteEnv() string     { return "" }
 func debugUseDerpRoute() opt.Bool      { return "" }

+ 35 - 0
wgengine/magicsock/dontfrag_darwin.go

@@ -0,0 +1,35 @@
+// Copyright (c) Tailscale Inc & AUTHORS
+// SPDX-License-Identifier: BSD-3-Clause
+
+//go:build darwin && !ios
+
+package magicsock
+
+import (
+	"net"
+	"syscall"
+
+	"golang.org/x/sys/unix"
+	"tailscale.com/types/nettype"
+)
+
+func setDontFragment(pconn nettype.PacketConn, network string) (err error) {
+	if c, ok := pconn.(*net.UDPConn); ok {
+		rc, err := c.SyscallConn()
+		if err == nil {
+			rc.Control(func(fd uintptr) {
+				if network == "udp4" {
+					err = syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IP, unix.IP_DONTFRAG, 1)
+				}
+				if network == "udp6" {
+					err = syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IPV6, unix.IPV6_DONTFRAG, 1)
+				}
+			})
+		}
+	}
+	return err
+}
+
+func CanPMTUD() bool {
+	return debugPMTUD() // only if the envknob is for now.
+}

+ 24 - 0
wgengine/magicsock/dontfrag_default.go

@@ -0,0 +1,24 @@
+// Copyright (c) Tailscale Inc & AUTHORS
+// SPDX-License-Identifier: BSD-3-Clause
+
+//go:build (!linux && !darwin) || android || ios
+
+package magicsock
+
+import (
+	"errors"
+
+	"tailscale.com/types/nettype"
+)
+
+// setDontFragment sets the dontfragment sockopt on pconn on the platforms that support it,
+// for both IPv4 and IPv6.
+// (C.f. https://datatracker.ietf.org/doc/html/rfc3542#section-11.2 for IPv6 fragmentation)
+func setDontFragment(pconn nettype.PacketConn, network string) (err error) {
+	return errors.New("setting don't fragment bit not supported on this OS")
+}
+
+// CanPMTUD returns whether this platform supports performing peet path MTU discovery.
+func CanPMTUD() bool {
+	return false
+}

+ 34 - 0
wgengine/magicsock/dontfrag_linux.go

@@ -0,0 +1,34 @@
+// Copyright (c) Tailscale Inc & AUTHORS
+// SPDX-License-Identifier: BSD-3-Clause
+
+//go:build linux && !android
+
+package magicsock
+
+import (
+	"net"
+	"syscall"
+
+	"tailscale.com/types/nettype"
+)
+
+func setDontFragment(pconn nettype.PacketConn, network string) (err error) {
+	if c, ok := pconn.(*net.UDPConn); ok {
+		rc, err := c.SyscallConn()
+		if err == nil {
+			rc.Control(func(fd uintptr) {
+				if network == "udp4" {
+					err = syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IP, syscall.IP_MTU_DISCOVER, syscall.IP_PMTUDISC_DO)
+				}
+				if network == "udp6" {
+					err = syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IPV6, syscall.IP_MTU_DISCOVER, syscall.IP_PMTUDISC_DO)
+				}
+			})
+		}
+	}
+	return err
+}
+
+func CanPMTUD() bool {
+	return debugPMTUD() // only if the envknob is enabled, for now.
+}

+ 10 - 0
wgengine/magicsock/magicsock.go

@@ -2233,6 +2233,16 @@ func (c *Conn) bindSocket(ruc *RebindingUDPConn, network string, curPortFate cur
 			continue
 		}
 		trySetSocketBuffer(pconn, c.logf)
+
+		if CanPMTUD() {
+			err = setDontFragment(pconn, network)
+			if err != nil {
+				c.logf("magicsock: set dontfragment failed for %v port %d: %v", network, port, err)
+				// TODO disable PMTUD in this case. We don't expect the setsockopt to fail on
+				// supported platforms, but we might as well be paranoid.
+			}
+		}
+
 		// Success.
 		if debugBindSocket() {
 			c.logf("magicsock: bindSocket: successfully listened %v port %d", network, port)