diff --git a/server/internal/selftest/mtu_linux.go b/server/internal/selftest/mtu_linux.go index d6e0dff..c273dd4 100644 --- a/server/internal/selftest/mtu_linux.go +++ b/server/internal/selftest/mtu_linux.go @@ -61,18 +61,27 @@ func probeEgressMTU(target string) MTUResult { _ = syscall.SetsockoptInt(fd, syscall.IPPROTO_IPV6, ipv6MTUDiscover, ipv6PMTUDiscDo) } - // Full-size probe: 1500 total − IP/UDP headers (28 v4, 48 v6). - payload := 1472 + // IP_MTU reflects the CONNECTED path's MTU, so the socket must be connected + // (an unconnected socket returns ENOTCONN). No handshake — UDP connect just + // pins the destination and resolves the route. sa := sockaddr(addr, 33434) + if err := syscall.Connect(fd, sa); err != nil { + res.Err = "connect: " + errStr(err) + return res + } + + // Full-size probe: 1500 total − IP/UDP headers (28 v4, 48 v6). A DF send + // larger than the local MTU fails immediately with EMSGSIZE; a path + // reduction updates IP_MTU after the ICMP frag-needed returns, so we send, + // briefly wait, and read the discovered MTU. + payload := 1472 if !is4 { payload = 1452 } - // A DF send larger than the local MTU fails immediately with EMSGSIZE; a - // path reduction updates IP_MTU after the ICMP frag-needed returns, so we - // send, briefly wait, and read the discovered MTU. - _ = syscall.Sendto(fd, make([]byte, payload), 0, sa) + probe := make([]byte, payload) + _, _ = syscall.Write(fd, probe) time.Sleep(700 * time.Millisecond) - _ = syscall.Sendto(fd, make([]byte, payload), 0, sa) // second send observes any reduction + _, _ = syscall.Write(fd, probe) // second send observes any reduction level, opt := syscall.IPPROTO_IP, ipMTU if !is4 {