From f3d346256d4ae3158f213639d13ba7bd9df7290c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Tue, 6 Sep 2022 00:44:41 +0800 Subject: [PATCH] Add dontfrag control --- common/control/frag_linux.go | 31 +++++++++++++++++++++++ common/control/frag_other.go | 7 ++++++ common/control/frag_unix.go | 28 +++++++++++++++++++++ common/control/frag_windows.go | 45 ++++++++++++++++++++++++++++++++++ 4 files changed, 111 insertions(+) create mode 100644 common/control/frag_linux.go create mode 100644 common/control/frag_other.go create mode 100644 common/control/frag_unix.go create mode 100644 common/control/frag_windows.go diff --git a/common/control/frag_linux.go b/common/control/frag_linux.go new file mode 100644 index 0000000..5cb5fca --- /dev/null +++ b/common/control/frag_linux.go @@ -0,0 +1,31 @@ +package control + +import ( + "os" + "syscall" + + N "github.com/sagernet/sing/common/network" + + "golang.org/x/sys/unix" +) + +func DisableUDPFragment() Func { + return func(network, address string, conn syscall.RawConn) error { + switch N.NetworkName(network) { + case N.NetworkUDP: + default: + return nil + } + return Raw(conn, func(fd uintptr) error { + if err := unix.SetsockoptInt(int(fd), unix.IPPROTO_IP, unix.IP_MTU_DISCOVER, unix.IP_PMTUDISC_DO); err != nil { + return os.NewSyscallError("SETSOCKOPT IP_MTU_DISCOVER IP_PMTUDISC_DO", err) + } + if network == "udp6" { + if err := unix.SetsockoptInt(int(fd), unix.IPPROTO_IPV6, unix.IPV6_MTU_DISCOVER, unix.IP_PMTUDISC_DO); err != nil { + return os.NewSyscallError("SETSOCKOPT IPV6_MTU_DISCOVER IP_PMTUDISC_DO", err) + } + } + return nil + }) + } +} diff --git a/common/control/frag_other.go b/common/control/frag_other.go new file mode 100644 index 0000000..68e0be5 --- /dev/null +++ b/common/control/frag_other.go @@ -0,0 +1,7 @@ +//go:build !((go1.19 && unix) || (!go1.19 && (linux || darwin)) || windows) + +package control + +func DisableUDPFragment() Func { + return nil +} diff --git a/common/control/frag_unix.go b/common/control/frag_unix.go new file mode 100644 index 0000000..3ee6130 --- /dev/null +++ b/common/control/frag_unix.go @@ -0,0 +1,28 @@ +//go:build (go1.19 && unix && !linux) || (!go1.19 && darwin) + +package control + +import ( + "os" + "syscall" + + "golang.org/x/sys/unix" +) + +func DisableUDPFragment() Func { + return func(network, address string, conn syscall.RawConn) error { + return Raw(conn, func(fd uintptr) error { + switch network { + case "udp4": + if err := unix.SetsockoptInt(int(fd), unix.IPPROTO_IP, unix.IP_DONTFRAG, 1); err != nil { + return os.NewSyscallError("SETSOCKOPT IP_DONTFRAG", err) + } + case "udp6": + if err := unix.SetsockoptInt(int(fd), unix.IPPROTO_IPV6, unix.IPV6_DONTFRAG, 1); err != nil { + return os.NewSyscallError("SETSOCKOPT IPV6_DONTFRAG", err) + } + } + return nil + }) + } +} diff --git a/common/control/frag_windows.go b/common/control/frag_windows.go new file mode 100644 index 0000000..bf02948 --- /dev/null +++ b/common/control/frag_windows.go @@ -0,0 +1,45 @@ +package control + +import ( + "os" + "syscall" + + N "github.com/sagernet/sing/common/network" + + "golang.org/x/sys/windows" +) + +const ( + IP_MTU_DISCOVER = 71 + IPV6_MTU_DISCOVER = 71 +) + +// enum PMTUD_STATE from ws2ipdef.h +const ( + IP_PMTUDISC_NOT_SET = iota + IP_PMTUDISC_DO + IP_PMTUDISC_DONT + IP_PMTUDISC_PROBE + IP_PMTUDISC_MAX +) + +func DisableUDPFragment() Func { + return func(network, address string, conn syscall.RawConn) error { + switch N.NetworkName(network) { + case N.NetworkUDP: + default: + return nil + } + return Raw(conn, func(fd uintptr) error { + if err := windows.SetsockoptInt(windows.Handle(fd), windows.IPPROTO_IP, IP_MTU_DISCOVER, IP_PMTUDISC_DO); err != nil { + return os.NewSyscallError("SETSOCKOPT IP_MTU_DISCOVER IP_PMTUDISC_DO", err) + } + if network == "udp6" { + if err := windows.SetsockoptInt(windows.Handle(fd), windows.IPPROTO_IPV6, IPV6_MTU_DISCOVER, IP_PMTUDISC_DO); err != nil { + return os.NewSyscallError("SETSOCKOPT IPV6_MTU_DISCOVER IP_PMTUDISC_DO", err) + } + } + return nil + }) + } +}