diff --git a/conn.go b/conn.go index 2f4e3a2398c..811279c9865 100644 --- a/conn.go +++ b/conn.go @@ -29,6 +29,19 @@ type OOBCapablePacketConn interface { var _ OOBCapablePacketConn = &net.UDPConn{} func wrapConn(pc net.PacketConn) (connection, error) { + conn, ok := pc.(interface { + SyscallConn() (syscall.RawConn, error) + }) + if ok { + rawConn, err := conn.SyscallConn() + if err != nil { + return nil, err + } + err = setDF(rawConn) + if err != nil { + return nil, err + } + } c, ok := pc.(OOBCapablePacketConn) if !ok { utils.DefaultLogger.Infof("PacketConn is not a net.UDPConn. Disabling optimizations possible on UDP connections.") diff --git a/err_size.go b/conn_df.go similarity index 60% rename from err_size.go rename to conn_df.go index a0638574050..ae9274d97fa 100644 --- a/err_size.go +++ b/conn_df.go @@ -3,6 +3,13 @@ package quic +import "syscall" + +func setDF(rawConn syscall.RawConn) error { + // no-op on unsupported platforms + return nil +} + func isMsgSizeErr(err error) bool { // to be implemented for more specific platforms return false diff --git a/conn_df_linux.go b/conn_df_linux.go new file mode 100644 index 00000000000..17ac67f12ad --- /dev/null +++ b/conn_df_linux.go @@ -0,0 +1,40 @@ +//go:build linux +// +build linux + +package quic + +import ( + "errors" + "syscall" + + "github.com/lucas-clemente/quic-go/internal/utils" + "golang.org/x/sys/unix" +) + +func setDF(rawConn syscall.RawConn) error { + // Enabling IP_MTU_DISCOVER will force the kernel to return "sendto: message too long" + // and the datagram will not be fragmented + var errDFIPv4, errDFIPv6 error + if err := rawConn.Control(func(fd uintptr) { + errDFIPv4 = unix.SetsockoptInt(int(fd), unix.IPPROTO_IP, unix.IP_MTU_DISCOVER, unix.IP_PMTUDISC_DO) + errDFIPv6 = unix.SetsockoptInt(int(fd), unix.IPPROTO_IPV6, unix.IPV6_MTU_DISCOVER, unix.IPV6_PMTUDISC_DO) + }); err != nil { + return err + } + switch { + case errDFIPv4 == nil && errDFIPv6 == nil: + utils.DefaultLogger.Debugf("Setting DF for IPv4 and IPv6.") + case errDFIPv4 == nil && errDFIPv6 != nil: + utils.DefaultLogger.Debugf("Setting DF for IPv4.") + case errDFIPv4 != nil && errDFIPv6 == nil: + utils.DefaultLogger.Debugf("Setting DF for IPv6.") + case errDFIPv4 != nil && errDFIPv6 != nil: + return errors.New("setting DF failed for both IPv4 and IPv6") + } + return nil +} + +func isMsgSizeErr(err error) bool { + // https://man7.org/linux/man-pages/man7/udp.7.html + return errors.Is(err, unix.EMSGSIZE) +} diff --git a/conn_df_windows.go b/conn_df_windows.go new file mode 100644 index 00000000000..4649f6463d2 --- /dev/null +++ b/conn_df_windows.go @@ -0,0 +1,46 @@ +//go:build windows +// +build windows + +package quic + +import ( + "errors" + "syscall" + + "github.com/lucas-clemente/quic-go/internal/utils" + "golang.org/x/sys/windows" +) + +const ( + // same for both IPv4 and IPv6 on Windows + // https://microsoft.github.io/windows-docs-rs/doc/windows/Win32/Networking/WinSock/constant.IP_DONTFRAG.html + // https://microsoft.github.io/windows-docs-rs/doc/windows/Win32/Networking/WinSock/constant.IPV6_DONTFRAG.html + IP_DONTFRAGMENT = 14 + IPV6_DONTFRAG = 14 +) + +func setDF(rawConn syscall.RawConn) error { + var errDFIPv4, errDFIPv6 error + if err := rawConn.Control(func(fd uintptr) { + errDFIPv4 = windows.SetsockoptInt(windows.Handle(fd), windows.IPPROTO_IP, IP_DONTFRAGMENT, 1) + errDFIPv6 = windows.SetsockoptInt(windows.Handle(fd), windows.IPPROTO_IPV6, IPV6_DONTFRAG, 1) + }); err != nil { + return err + } + switch { + case errDFIPv4 == nil && errDFIPv6 == nil: + utils.DefaultLogger.Debugf("Setting DF for IPv4 and IPv6.") + case errDFIPv4 == nil && errDFIPv6 != nil: + utils.DefaultLogger.Debugf("Setting DF for IPv4.") + case errDFIPv4 != nil && errDFIPv6 == nil: + utils.DefaultLogger.Debugf("Setting DF for IPv6.") + case errDFIPv4 != nil && errDFIPv6 != nil: + return errors.New("setting DF failed for both IPv4 and IPv6") + } + return nil +} + +func isMsgSizeErr(err error) bool { + // https://docs.microsoft.com/en-us/windows/win32/winsock/windows-sockets-error-codes-2 + return errors.Is(err, windows.WSAEMSGSIZE) +} diff --git a/conn_oob.go b/conn_oob.go index f1aebfaa354..b46781377d2 100644 --- a/conn_oob.go +++ b/conn_oob.go @@ -87,8 +87,6 @@ func newConn(c OOBCapablePacketConn) (*oobConn, error) { errPIIPv4 = unix.SetsockoptInt(int(fd), unix.IPPROTO_IP, ipv4RECVPKTINFO, 1) errPIIPv6 = unix.SetsockoptInt(int(fd), unix.IPPROTO_IPV6, ipv6RECVPKTINFO, 1) } - - setOOBSockOpts(fd) }); err != nil { return nil, err } diff --git a/conn_oob_opts.go b/conn_oob_opts.go deleted file mode 100644 index 4d29c7c4c1f..00000000000 --- a/conn_oob_opts.go +++ /dev/null @@ -1,8 +0,0 @@ -//go:build !linux && !windows -// +build !linux,!windows - -package quic - -func setOOBSockOpts(fd uintptr) { - // no-op on unsupported platforms -} diff --git a/conn_oob_opts_linux.go b/conn_oob_opts_linux.go deleted file mode 100644 index 81e7ca58e69..00000000000 --- a/conn_oob_opts_linux.go +++ /dev/null @@ -1,15 +0,0 @@ -//go:build linux -// +build linux - -package quic - -import ( - "golang.org/x/sys/unix" -) - -func setOOBSockOpts(fd uintptr) { - // Enabling IP_MTU_DISCOVER will force the kernel to return "sendto: message too long" - // and the datagram will not be fragmented - unix.SetsockoptInt(int(fd), unix.IPPROTO_IP, unix.IP_MTU_DISCOVER, unix.IP_PMTUDISC_DO) - unix.SetsockoptInt(int(fd), unix.IPPROTO_IPV6, unix.IPV6_MTU_DISCOVER, unix.IPV6_PMTUDISC_DO) -} diff --git a/conn_windows.go b/conn_windows.go index 72906b8950b..4869d67aaa2 100644 --- a/conn_windows.go +++ b/conn_windows.go @@ -9,40 +9,10 @@ import ( "net" "syscall" - "github.com/lucas-clemente/quic-go/internal/utils" "golang.org/x/sys/windows" ) -const ( - // same for both IPv4 and IPv6 on Windows - // https://microsoft.github.io/windows-docs-rs/doc/windows/Win32/Networking/WinSock/constant.IP_DONTFRAG.html - // https://microsoft.github.io/windows-docs-rs/doc/windows/Win32/Networking/WinSock/constant.IPV6_DONTFRAG.html - IP_DONTFRAGMENT = 14 - IPV6_DONTFRAG = 14 -) - func newConn(c OOBCapablePacketConn) (connection, error) { - rawConn, err := c.SyscallConn() - if err != nil { - return nil, fmt.Errorf("couldn't get syscall.RawConn: %w", err) - } - var errDFIPv4, errDFIPv6 error - if err := rawConn.Control(func(fd uintptr) { - errDFIPv4 = windows.SetsockoptInt(windows.Handle(fd), windows.IPPROTO_IP, IP_DONTFRAGMENT, 1) - errDFIPv6 = windows.SetsockoptInt(windows.Handle(fd), windows.IPPROTO_IPV6, IPV6_DONTFRAG, 1) - }); err != nil { - return nil, err - } - switch { - case errDFIPv4 == nil && errDFIPv6 == nil: - utils.DefaultLogger.Debugf("Setting DF for IPv4 and IPv6.") - case errDFIPv4 == nil && errDFIPv6 != nil: - utils.DefaultLogger.Debugf("Setting DF for IPv4.") - case errDFIPv4 != nil && errDFIPv6 == nil: - utils.DefaultLogger.Debugf("Setting DF for IPv6.") - case errDFIPv4 != nil && errDFIPv6 != nil: - return nil, errors.New("setting Df failed for both IPv4 and IPv6") - } return &basicConn{PacketConn: c}, nil } diff --git a/err_size_linux.go b/err_size_linux.go deleted file mode 100644 index aa020809110..00000000000 --- a/err_size_linux.go +++ /dev/null @@ -1,15 +0,0 @@ -//go:build linux -// +build linux - -package quic - -import ( - "errors" - - "golang.org/x/sys/unix" -) - -func isMsgSizeErr(err error) bool { - // https://man7.org/linux/man-pages/man7/udp.7.html - return errors.Is(err, unix.EMSGSIZE) -} diff --git a/err_size_windows.go b/err_size_windows.go deleted file mode 100644 index 46e72f2466a..00000000000 --- a/err_size_windows.go +++ /dev/null @@ -1,15 +0,0 @@ -//go:build windows -// +build windows - -package quic - -import ( - "errors" - - "golang.org/x/sys/windows" -) - -func isMsgSizeErr(err error) bool { - // https://docs.microsoft.com/en-us/windows/win32/winsock/windows-sockets-error-codes-2 - return errors.Is(err, windows.WSAEMSGSIZE) -}