From bede4fb3a082e4f62d7a50cd09336b306fda8dc9 Mon Sep 17 00:00:00 2001 From: Spike Curtis Date: Mon, 9 Jun 2025 08:44:32 +0400 Subject: [PATCH 1/8] feat: pad disco ping/pong and enable path mtu on Windows --- disco/disco.go | 19 +++++-- disco/disco_test.go | 75 +++++++++++++++++++++++-- wgengine/magicsock/magicsock.go | 1 + wgengine/magicsock/magicsock_darwin.go | 6 ++ wgengine/magicsock/magicsock_linux.go | 5 ++ wgengine/magicsock/magicsock_windows.go | 48 ++++++++++++++++ 6 files changed, 142 insertions(+), 12 deletions(-) create mode 100644 wgengine/magicsock/magicsock_darwin.go create mode 100644 wgengine/magicsock/magicsock_windows.go diff --git a/disco/disco.go b/disco/disco.go index 0e7c3f7e5f882..6a55d15bebdb7 100644 --- a/disco/disco.go +++ b/disco/disco.go @@ -27,6 +27,7 @@ import ( "net/netip" "go4.org/mem" + "golang.org/x/crypto/nacl/box" "tailscale.com/types/key" ) @@ -48,6 +49,16 @@ const ( const v0 = byte(0) +// v1 Ping and Pong are padded as follows. CallMeMaybe is still on v0 and unpadded. +const v1 = byte(1) + +// paddedPayloadLen is the desired length we want to pad Ping and Pong payloads +// to so that they are the maximum size of a Wireguard packet we would +// subsequently send. +// Our inner IP packets can be up to 1280 bytes, with the Wireguard header of +// 30 bytes, that is 1310. The final 2 is the inner payload header's type and version. +const paddedPayloadLen = 1310 - len(Magic) - keyLen - NonceLen - box.Overhead - 2 + var errShort = errors.New("short message") // LooksLikeDiscoWrapper reports whether p looks like it's a packet @@ -120,12 +131,8 @@ type Ping struct { } func (m *Ping) AppendMarshal(b []byte) []byte { - dataLen := 12 hasKey := !m.NodeKey.IsZero() - if hasKey { - dataLen += key.NodePublicRawLen - } - ret, d := appendMsgHeader(b, TypePing, v0, dataLen) + ret, d := appendMsgHeader(b, TypePing, v1, paddedPayloadLen) n := copy(d, m.TxID[:]) if hasKey { m.NodeKey.AppendTo(d[:n]) @@ -217,7 +224,7 @@ type Pong struct { const pongLen = 12 + 16 + 2 func (m *Pong) AppendMarshal(b []byte) []byte { - ret, d := appendMsgHeader(b, TypePong, v0, pongLen) + ret, d := appendMsgHeader(b, TypePong, v1, paddedPayloadLen) d = d[copy(d, m.TxID[:]):] ip16 := m.Src.Addr().As16() d = d[copy(d, ip16[:]):] diff --git a/disco/disco_test.go b/disco/disco_test.go index 67bd1561a9bf6..39c34b4e664cc 100644 --- a/disco/disco_test.go +++ b/disco/disco_test.go @@ -25,7 +25,7 @@ func TestMarshalAndParse(t *testing.T) { m: &Ping{ TxID: [12]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}, }, - want: "01 00 01 02 03 04 05 06 07 08 09 0a 0b 0c", + want: "01 01 01 02 03 04 05 06 07 08 09 0a 0b 0c", }, { name: "ping_with_nodekey_src", @@ -33,7 +33,7 @@ func TestMarshalAndParse(t *testing.T) { TxID: [12]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}, NodeKey: key.NodePublicFromRaw32(mem.B([]byte{1: 1, 2: 2, 30: 30, 31: 31})), }, - want: "01 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 00 01 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 1e 1f", + want: "01 01 01 02 03 04 05 06 07 08 09 0a 0b 0c 00 01 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 1e 1f", }, { name: "pong", @@ -41,7 +41,7 @@ func TestMarshalAndParse(t *testing.T) { TxID: [12]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}, Src: mustIPPort("2.3.4.5:1234"), }, - want: "02 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 00 00 00 00 00 00 00 00 00 00 ff ff 02 03 04 05 04 d2", + want: "02 01 01 02 03 04 05 06 07 08 09 0a 0b 0c 00 00 00 00 00 00 00 00 00 00 ff ff 02 03 04 05 04 d2", }, { name: "pongv6", @@ -49,7 +49,7 @@ func TestMarshalAndParse(t *testing.T) { TxID: [12]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}, Src: mustIPPort("[fed0::12]:6666"), }, - want: "02 00 01 02 03 04 05 06 07 08 09 0a 0b 0c fe d0 00 00 00 00 00 00 00 00 00 00 00 00 00 12 1a 0a", + want: "02 01 01 02 03 04 05 06 07 08 09 0a 0b 0c fe d0 00 00 00 00 00 00 00 00 00 00 00 00 00 12 1a 0a", }, { name: "call_me_maybe", @@ -77,8 +77,8 @@ func TestMarshalAndParse(t *testing.T) { } gotHex := fmt.Sprintf("% x", got) - if gotHex != tt.want { - t.Fatalf("wrong marshal\n got: %s\nwant: %s\n", gotHex, tt.want) + if !strings.HasPrefix(gotHex, tt.want) { + t.Fatalf("wrong marshal\n got: %s\nwant prefix: %s\n", gotHex, tt.want) } back, err := Parse([]byte(got)) @@ -92,6 +92,69 @@ func TestMarshalAndParse(t *testing.T) { } } +func TestParsePingPongV0(t *testing.T) { + tests := []struct { + name string + payload []byte + m Message + }{ + { + name: "ping", + m: &Ping{ + TxID: [12]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}, + }, + payload: []byte{0x01, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c}, + }, + { + name: "ping_with_nodekey_src", + m: &Ping{ + TxID: [12]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}, + NodeKey: key.NodePublicFromRaw32(mem.B([]byte{1: 1, 2: 2, 30: 30, 31: 31})), + }, + payload: []byte{ + 0x01, 0x00, + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, + 0x00, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x1f}, + }, + { + name: "pong", + m: &Pong{ + TxID: [12]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}, + Src: mustIPPort("2.3.4.5:1234"), + }, + payload: []byte{ + 0x02, 0x00, + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x02, 0x03, 0x04, 0x05, + 0x04, 0xd2}, + }, + { + name: "pongv6", + m: &Pong{ + TxID: [12]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}, + Src: mustIPPort("[fed0::12]:6666"), + }, + payload: []byte{ + 0x02, 0x00, + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, + 0xfe, 0xd0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, + 0x1a, 0x0a}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + back, err := Parse(tt.payload) + if err != nil { + t.Fatalf("parse back: %v", err) + } + if !reflect.DeepEqual(back, tt.m) { + t.Errorf("message in %+v doesn't match Parse result %+v", tt.m, back) + } + }) + } +} + func mustIPPort(s string) netip.AddrPort { ipp, err := netip.ParseAddrPort(s) if err != nil { diff --git a/wgengine/magicsock/magicsock.go b/wgengine/magicsock/magicsock.go index 3c77a11353012..18d908a510104 100644 --- a/wgengine/magicsock/magicsock.go +++ b/wgengine/magicsock/magicsock.go @@ -2377,6 +2377,7 @@ func (c *Conn) bindSocket(ruc *RebindingUDPConn, network string, curPortFate cur continue } trySetSocketBuffer(pconn, c.logf) + trySetPathMTUDiscover(pconn, c.logf, network) // Success. if debugBindSocket() { c.logf("magicsock: bindSocket: successfully listened %v port %d", network, port) diff --git a/wgengine/magicsock/magicsock_darwin.go b/wgengine/magicsock/magicsock_darwin.go new file mode 100644 index 0000000000000..d4d0384bb4a2a --- /dev/null +++ b/wgengine/magicsock/magicsock_darwin.go @@ -0,0 +1,6 @@ +package magicsock + +func trySetPathMTUDiscover(pconn nettype.PacketConn, logf logger.Logf, network string) { + // TODO: implement + logf("magicsock: failed to set Path MTU Discover: not implemented") +} diff --git a/wgengine/magicsock/magicsock_linux.go b/wgengine/magicsock/magicsock_linux.go index a4101ccbaa69d..820cd9fa24aff 100644 --- a/wgengine/magicsock/magicsock_linux.go +++ b/wgengine/magicsock/magicsock_linux.go @@ -404,3 +404,8 @@ func init() { // message. These contain a single uint16 of data. controlMessageSize = unix.CmsgSpace(2) } + +func trySetPathMTUDiscover(pconn nettype.PacketConn, logf logger.Logf, network string) { + // TODO: implement + logf("magicsock: failed to set Path MTU Discover: not implemented") +} diff --git a/wgengine/magicsock/magicsock_windows.go b/wgengine/magicsock/magicsock_windows.go new file mode 100644 index 0000000000000..69b6faf755eb2 --- /dev/null +++ b/wgengine/magicsock/magicsock_windows.go @@ -0,0 +1,48 @@ +package magicsock + +import ( + "net" + + "golang.org/x/sys/windows" + "tailscale.com/types/logger" + "tailscale.com/types/nettype" +) + +// https://github.com/tpn/winsdk-10/blob/9b69fd26ac0c7d0b83d378dba01080e93349c2ed/Include/10.0.16299.0/shared/ws2ipdef.h +const ( + IP_MTU_DISCOVER = 71 // IPV6_MTU_DISCOVER has the same value, which is nice. +) + +const ( + IP_PMTUDISC_NOT_SET = iota + IP_PMTUDISC_DO + IP_PMTUDISC_DONT + IP_PMTUDISC_PROBE + IP_PMTUDISC_MAX +) + +func trySetPathMTUDiscover(pconn nettype.PacketConn, logf logger.Logf, network string) { + logf("setting Path MTU Discover on %s", pconn.LocalAddr().String()) + if c, ok := pconn.(*net.UDPConn); ok { + s, err := c.SyscallConn() + if err != nil { + logf("magicsock: failed to set Path MTU Discover: get syscall conn: %v", err) + } + level := windows.IPPROTO_IP + if network == "udp6" { + level = windows.IPPROTO_IPV6 + } + err = s.Control(func(fd uintptr) { + err := windows.SetsockoptInt(windows.Handle(fd), level, IP_MTU_DISCOVER, IP_PMTUDISC_DO) + if err != nil { + logf("magicsock: failed to set Path MTU Discover: SetsockoptInt failed: %v", err) + } + }) + if err != nil { + logf("magicsock: failed to set Path MTU Discover: control connection: %v", err) + } + logf("sucessfully set Path MTU Discover on %s", pconn.LocalAddr().String()) + return + } + logf("magicsock: failed to set Path MTU Discover: not a UDPConn") +} From 1bab46fdb716a46f238f19bdab4812c882e70caa Mon Sep 17 00:00:00 2001 From: Spike Curtis Date: Mon, 9 Jun 2025 06:28:27 +0000 Subject: [PATCH 2/8] feat: set path MTU discover on Linux --- wgengine/magicsock/magicsock_darwin.go | 34 +++++++++++++++++++++++-- wgengine/magicsock/magicsock_linux.go | 26 +++++++++++++++++-- wgengine/magicsock/magicsock_windows.go | 3 +-- 3 files changed, 57 insertions(+), 6 deletions(-) diff --git a/wgengine/magicsock/magicsock_darwin.go b/wgengine/magicsock/magicsock_darwin.go index d4d0384bb4a2a..e165a443b1623 100644 --- a/wgengine/magicsock/magicsock_darwin.go +++ b/wgengine/magicsock/magicsock_darwin.go @@ -1,6 +1,36 @@ package magicsock +import ( + "net" + + "golang.org/x/sys/unix" + "tailscale.com/types/logger" + "tailscale.com/types/nettype" +) + func trySetPathMTUDiscover(pconn nettype.PacketConn, logf logger.Logf, network string) { - // TODO: implement - logf("magicsock: failed to set Path MTU Discover: not implemented") + if c, ok := pconn.(*net.UDPConn); ok { + s, err := c.SyscallConn() + if err != nil { + logf("magicsock: failed to set Path MTU Discover: get syscall conn: %v", err) + } + level := unix.IPPROTO_IP + option := unix.IP_MTU_DISCOVER + if network == "udp6" { + level = unix.IPPROTO_IPV6 + option = unix.IPV6_MTU_DISCOVER + } + err = s.Control(func(fd uintptr) { + err := unix.SetsockoptInt(int(fd), level, option, unix.IP_PMTUDISC_DO) + if err != nil { + logf("magicsock: failed to set Path MTU Discover: SetsockoptInt failed: %v", err) + } + }) + if err != nil { + logf("magicsock: failed to set Path MTU Discover: control connection: %v", err) + } + logf("magicsock: successfully set Path MTU Discover on %s", pconn.LocalAddr().String()) + return + } + logf("magicsock: failed to set Path MTU Discover: not a UDPConn") } diff --git a/wgengine/magicsock/magicsock_linux.go b/wgengine/magicsock/magicsock_linux.go index 820cd9fa24aff..c7c921f6435ca 100644 --- a/wgengine/magicsock/magicsock_linux.go +++ b/wgengine/magicsock/magicsock_linux.go @@ -406,6 +406,28 @@ func init() { } func trySetPathMTUDiscover(pconn nettype.PacketConn, logf logger.Logf, network string) { - // TODO: implement - logf("magicsock: failed to set Path MTU Discover: not implemented") + if c, ok := pconn.(*net.UDPConn); ok { + s, err := c.SyscallConn() + if err != nil { + logf("magicsock: failed to set Path MTU Discover: get syscall conn: %v", err) + } + level := unix.IPPROTO_IP + option := unix.IP_MTU_DISCOVER + if network == "udp6" { + level = unix.IPPROTO_IPV6 + option = unix.IPV6_MTU_DISCOVER + } + err = s.Control(func(fd uintptr) { + err := unix.SetsockoptInt(int(fd), level, option, unix.IP_PMTUDISC_DO) + if err != nil { + logf("magicsock: failed to set Path MTU Discover: SetsockoptInt failed: %v", err) + } + }) + if err != nil { + logf("magicsock: failed to set Path MTU Discover: control connection: %v", err) + } + logf("magicsock: successfully set Path MTU Discover on %s", pconn.LocalAddr().String()) + return + } + logf("magicsock: failed to set Path MTU Discover: not a UDPConn") } diff --git a/wgengine/magicsock/magicsock_windows.go b/wgengine/magicsock/magicsock_windows.go index 69b6faf755eb2..feccf57eac24d 100644 --- a/wgengine/magicsock/magicsock_windows.go +++ b/wgengine/magicsock/magicsock_windows.go @@ -22,7 +22,6 @@ const ( ) func trySetPathMTUDiscover(pconn nettype.PacketConn, logf logger.Logf, network string) { - logf("setting Path MTU Discover on %s", pconn.LocalAddr().String()) if c, ok := pconn.(*net.UDPConn); ok { s, err := c.SyscallConn() if err != nil { @@ -41,7 +40,7 @@ func trySetPathMTUDiscover(pconn nettype.PacketConn, logf logger.Logf, network s if err != nil { logf("magicsock: failed to set Path MTU Discover: control connection: %v", err) } - logf("sucessfully set Path MTU Discover on %s", pconn.LocalAddr().String()) + logf("magicsock: successfully set Path MTU Discover on %s", pconn.LocalAddr().String()) return } logf("magicsock: failed to set Path MTU Discover: not a UDPConn") From ef3f81d39f70361a38d69c2bcb30cf3f754365a0 Mon Sep 17 00:00:00 2001 From: Spike Curtis Date: Mon, 9 Jun 2025 11:12:18 +0400 Subject: [PATCH 3/8] feat: set IP_DONTFRAG on macOS --- wgengine/magicsock/magicsock_darwin.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/wgengine/magicsock/magicsock_darwin.go b/wgengine/magicsock/magicsock_darwin.go index e165a443b1623..47ae3404976bb 100644 --- a/wgengine/magicsock/magicsock_darwin.go +++ b/wgengine/magicsock/magicsock_darwin.go @@ -15,13 +15,13 @@ func trySetPathMTUDiscover(pconn nettype.PacketConn, logf logger.Logf, network s logf("magicsock: failed to set Path MTU Discover: get syscall conn: %v", err) } level := unix.IPPROTO_IP - option := unix.IP_MTU_DISCOVER + option := unix.IP_DONTFRAG if network == "udp6" { level = unix.IPPROTO_IPV6 - option = unix.IPV6_MTU_DISCOVER + option = unix.IPV6_DONTFRAG } err = s.Control(func(fd uintptr) { - err := unix.SetsockoptInt(int(fd), level, option, unix.IP_PMTUDISC_DO) + err := unix.SetsockoptInt(int(fd), level, option, 1) if err != nil { logf("magicsock: failed to set Path MTU Discover: SetsockoptInt failed: %v", err) } From c89c6b7551639ca48bff53754c9c5367bf68cfcf Mon Sep 17 00:00:00 2001 From: Spike Curtis Date: Mon, 9 Jun 2025 07:31:27 +0000 Subject: [PATCH 4/8] logs and comments --- disco/disco.go | 5 ++++- wgengine/magicsock/magicsock.go | 7 ++++++- wgengine/magicsock/magicsock_darwin.go | 12 ++++++------ wgengine/magicsock/magicsock_linux.go | 12 ++++++------ wgengine/magicsock/magicsock_windows.go | 12 ++++++------ 5 files changed, 28 insertions(+), 20 deletions(-) diff --git a/disco/disco.go b/disco/disco.go index 6a55d15bebdb7..8badad2dc895f 100644 --- a/disco/disco.go +++ b/disco/disco.go @@ -54,7 +54,10 @@ const v1 = byte(1) // paddedPayloadLen is the desired length we want to pad Ping and Pong payloads // to so that they are the maximum size of a Wireguard packet we would -// subsequently send. +// subsequently send. This ensures that any UDP paths we discover will actually +// support the packet sizes the net stack will send over those paths. Any peers +// behind a small-MTU link will have to depend on DERP. +// c.f. https://github.com/coder/coder/issues/15523 // Our inner IP packets can be up to 1280 bytes, with the Wireguard header of // 30 bytes, that is 1310. The final 2 is the inner payload header's type and version. const paddedPayloadLen = 1310 - len(Magic) - keyLen - NonceLen - box.Overhead - 2 diff --git a/wgengine/magicsock/magicsock.go b/wgengine/magicsock/magicsock.go index 18d908a510104..fc70b8ae6080c 100644 --- a/wgengine/magicsock/magicsock.go +++ b/wgengine/magicsock/magicsock.go @@ -2377,7 +2377,12 @@ func (c *Conn) bindSocket(ruc *RebindingUDPConn, network string, curPortFate cur continue } trySetSocketBuffer(pconn, c.logf) - trySetPathMTUDiscover(pconn, c.logf, network) + // CODER: https://github.com/coder/coder/issues/15523 + // Attempt to tell the OS not to fragment packets over this interface. We pad disco Ping and Pong packets to the + // size of the direct UDP packets that get sent for direct connections. Thus, any interfaces or paths that + // cannot fully support direct connections due to MTU limitations will not be selected. If no direct paths meet + // the MTU requirements for a peer, we will fall back to DERP for that peer. + tryPreventFragmentation(pconn, c.logf, network) // Success. if debugBindSocket() { c.logf("magicsock: bindSocket: successfully listened %v port %d", network, port) diff --git a/wgengine/magicsock/magicsock_darwin.go b/wgengine/magicsock/magicsock_darwin.go index 47ae3404976bb..f3b32959f5ba8 100644 --- a/wgengine/magicsock/magicsock_darwin.go +++ b/wgengine/magicsock/magicsock_darwin.go @@ -8,11 +8,11 @@ import ( "tailscale.com/types/nettype" ) -func trySetPathMTUDiscover(pconn nettype.PacketConn, logf logger.Logf, network string) { +func tryPreventFragmentation(pconn nettype.PacketConn, logf logger.Logf, network string) { if c, ok := pconn.(*net.UDPConn); ok { s, err := c.SyscallConn() if err != nil { - logf("magicsock: failed to set Path MTU Discover: get syscall conn: %v", err) + logf("magicsock: dontfrag: failed to get syscall conn: %v", err) } level := unix.IPPROTO_IP option := unix.IP_DONTFRAG @@ -23,14 +23,14 @@ func trySetPathMTUDiscover(pconn nettype.PacketConn, logf logger.Logf, network s err = s.Control(func(fd uintptr) { err := unix.SetsockoptInt(int(fd), level, option, 1) if err != nil { - logf("magicsock: failed to set Path MTU Discover: SetsockoptInt failed: %v", err) + logf("magicsock: dontfrag: SetsockoptInt failed: %v", err) } }) if err != nil { - logf("magicsock: failed to set Path MTU Discover: control connection: %v", err) + logf("magicsock: dontfrag: control connection failed: %v", err) } - logf("magicsock: successfully set Path MTU Discover on %s", pconn.LocalAddr().String()) + logf("magicsock: dontfrag: success on %s", pconn.LocalAddr().String()) return } - logf("magicsock: failed to set Path MTU Discover: not a UDPConn") + logf("magicsock: dontfrag: failed because it was not a UDPConn") } diff --git a/wgengine/magicsock/magicsock_linux.go b/wgengine/magicsock/magicsock_linux.go index c7c921f6435ca..f1be3d1cf40e0 100644 --- a/wgengine/magicsock/magicsock_linux.go +++ b/wgengine/magicsock/magicsock_linux.go @@ -405,11 +405,11 @@ func init() { controlMessageSize = unix.CmsgSpace(2) } -func trySetPathMTUDiscover(pconn nettype.PacketConn, logf logger.Logf, network string) { +func tryPreventFragmentation(pconn nettype.PacketConn, logf logger.Logf, network string) { if c, ok := pconn.(*net.UDPConn); ok { s, err := c.SyscallConn() if err != nil { - logf("magicsock: failed to set Path MTU Discover: get syscall conn: %v", err) + logf("magicsock: dontfrag: failed to get syscall conn: %v", err) } level := unix.IPPROTO_IP option := unix.IP_MTU_DISCOVER @@ -420,14 +420,14 @@ func trySetPathMTUDiscover(pconn nettype.PacketConn, logf logger.Logf, network s err = s.Control(func(fd uintptr) { err := unix.SetsockoptInt(int(fd), level, option, unix.IP_PMTUDISC_DO) if err != nil { - logf("magicsock: failed to set Path MTU Discover: SetsockoptInt failed: %v", err) + logf("magicsock: dontfrag: SetsockoptInt failed: %v", err) } }) if err != nil { - logf("magicsock: failed to set Path MTU Discover: control connection: %v", err) + logf("magicsock: dontfrag: control connection failed: %v", err) } - logf("magicsock: successfully set Path MTU Discover on %s", pconn.LocalAddr().String()) + logf("magicsock: dontfrag: success on %s", pconn.LocalAddr().String()) return } - logf("magicsock: failed to set Path MTU Discover: not a UDPConn") + logf("magicsock: dontfrag: failed because it was not a UDPConn") } diff --git a/wgengine/magicsock/magicsock_windows.go b/wgengine/magicsock/magicsock_windows.go index feccf57eac24d..622b7f0acabf6 100644 --- a/wgengine/magicsock/magicsock_windows.go +++ b/wgengine/magicsock/magicsock_windows.go @@ -21,11 +21,11 @@ const ( IP_PMTUDISC_MAX ) -func trySetPathMTUDiscover(pconn nettype.PacketConn, logf logger.Logf, network string) { +func tryPreventFragmentation(pconn nettype.PacketConn, logf logger.Logf, network string) { if c, ok := pconn.(*net.UDPConn); ok { s, err := c.SyscallConn() if err != nil { - logf("magicsock: failed to set Path MTU Discover: get syscall conn: %v", err) + logf("magicsock: dontfrag: failed to get syscall conn: %v", err) } level := windows.IPPROTO_IP if network == "udp6" { @@ -34,14 +34,14 @@ func trySetPathMTUDiscover(pconn nettype.PacketConn, logf logger.Logf, network s err = s.Control(func(fd uintptr) { err := windows.SetsockoptInt(windows.Handle(fd), level, IP_MTU_DISCOVER, IP_PMTUDISC_DO) if err != nil { - logf("magicsock: failed to set Path MTU Discover: SetsockoptInt failed: %v", err) + logf("magicsock: dontfrag: SetsockoptInt failed: %v", err) } }) if err != nil { - logf("magicsock: failed to set Path MTU Discover: control connection: %v", err) + logf("magicsock: dontfrag: control connection failed: %v", err) } - logf("magicsock: successfully set Path MTU Discover on %s", pconn.LocalAddr().String()) + logf("magicsock: dontfrag: success on %s", pconn.LocalAddr().String()) return } - logf("magicsock: failed to set Path MTU Discover: not a UDPConn") + logf("magicsock: dontfrag: failed because it was not a UDPConn") } From 4686156560d6a453d163677d00f8af31824fa63f Mon Sep 17 00:00:00 2001 From: Spike Curtis Date: Tue, 10 Jun 2025 09:58:53 +0400 Subject: [PATCH 5/8] fix: check Disco Ping/Pong length in unit tests --- disco/disco_test.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/disco/disco_test.go b/disco/disco_test.go index 39c34b4e664cc..426d06e0c0ab1 100644 --- a/disco/disco_test.go +++ b/disco/disco_test.go @@ -5,6 +5,7 @@ package disco import ( "fmt" + "golang.org/x/crypto/nacl/box" "net/netip" "reflect" "strings" @@ -75,6 +76,11 @@ func TestMarshalAndParse(t *testing.T) { if !ok { t.Fatalf("didn't start with foo: got %q", got) } + // CODER: 1310 is max size of a Wireguard packet we will send. + expectedLen := 1310 - len(Magic) - keyLen - NonceLen - box.Overhead + if _, ok := tt.m.(*CallMeMaybe); !ok && len(got) != expectedLen { + t.Fatalf("Ping/Pong not padded: got len %d, want len %d", len(got), expectedLen) + } gotHex := fmt.Sprintf("% x", got) if !strings.HasPrefix(gotHex, tt.want) { From 6923084c486984f0c74ac2adb1df181bedf1368b Mon Sep 17 00:00:00 2001 From: Spike Curtis Date: Tue, 10 Jun 2025 10:00:38 +0400 Subject: [PATCH 6/8] restyle constant defs on Windows --- wgengine/magicsock/magicsock_windows.go | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/wgengine/magicsock/magicsock_windows.go b/wgengine/magicsock/magicsock_windows.go index 622b7f0acabf6..0206fbc47c896 100644 --- a/wgengine/magicsock/magicsock_windows.go +++ b/wgengine/magicsock/magicsock_windows.go @@ -11,14 +11,7 @@ import ( // https://github.com/tpn/winsdk-10/blob/9b69fd26ac0c7d0b83d378dba01080e93349c2ed/Include/10.0.16299.0/shared/ws2ipdef.h const ( IP_MTU_DISCOVER = 71 // IPV6_MTU_DISCOVER has the same value, which is nice. -) - -const ( - IP_PMTUDISC_NOT_SET = iota - IP_PMTUDISC_DO - IP_PMTUDISC_DONT - IP_PMTUDISC_PROBE - IP_PMTUDISC_MAX + IP_PMTUDISC_DO = 1 ) func tryPreventFragmentation(pconn nettype.PacketConn, logf logger.Logf, network string) { From b824a9c119b4d2edbad453ad73212dfd21a5aa67 Mon Sep 17 00:00:00 2001 From: Spike Curtis Date: Tue, 10 Jun 2025 14:47:03 +0400 Subject: [PATCH 7/8] make ping/pong test explicit --- disco/disco_test.go | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/disco/disco_test.go b/disco/disco_test.go index 426d06e0c0ab1..439a3077ea8ca 100644 --- a/disco/disco_test.go +++ b/disco/disco_test.go @@ -78,8 +78,16 @@ func TestMarshalAndParse(t *testing.T) { } // CODER: 1310 is max size of a Wireguard packet we will send. expectedLen := 1310 - len(Magic) - keyLen - NonceLen - box.Overhead - if _, ok := tt.m.(*CallMeMaybe); !ok && len(got) != expectedLen { - t.Fatalf("Ping/Pong not padded: got len %d, want len %d", len(got), expectedLen) + switch tt.m.(type) { + case *Ping: + if len(got) != expectedLen { + t.Fatalf("Ping not padded: got len %d, want len %d", len(got), expectedLen) + } + case *Pong: + if len(got) != expectedLen { + t.Fatalf("Pong not padded: got len %d, want len %d", len(got), expectedLen) + } + // CallMeMaybe is unpadded } gotHex := fmt.Sprintf("% x", got) From 4ec7ef5a171b1b848a3aa0f3eef0b541f95ec337 Mon Sep 17 00:00:00 2001 From: Spike Curtis Date: Tue, 10 Jun 2025 14:49:03 +0400 Subject: [PATCH 8/8] import ordering --- disco/disco_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/disco/disco_test.go b/disco/disco_test.go index 439a3077ea8ca..475203e0aa1d2 100644 --- a/disco/disco_test.go +++ b/disco/disco_test.go @@ -5,13 +5,13 @@ package disco import ( "fmt" - "golang.org/x/crypto/nacl/box" "net/netip" "reflect" "strings" "testing" "go4.org/mem" + "golang.org/x/crypto/nacl/box" "tailscale.com/types/key" )