diff options
Diffstat (limited to 'src/net/mptcpsock_linux.go')
-rw-r--r-- | src/net/mptcpsock_linux.go | 51 |
1 files changed, 51 insertions, 0 deletions
diff --git a/src/net/mptcpsock_linux.go b/src/net/mptcpsock_linux.go index 15a7882498..e1a78fd59f 100644 --- a/src/net/mptcpsock_linux.go +++ b/src/net/mptcpsock_linux.go @@ -8,6 +8,7 @@ import ( "context" "errors" "internal/poll" + "internal/syscall/unix" "sync" "syscall" ) @@ -15,11 +16,14 @@ import ( var ( mptcpOnce sync.Once mptcpAvailable bool + hasSOLMPTCP bool ) // These constants aren't in the syscall package, which is frozen const ( _IPPROTO_MPTCP = 0x106 + _SOL_MPTCP = 0x11c + _MPTCP_INFO = 0x1 ) func supportsMultipathTCP() bool { @@ -41,6 +45,10 @@ func initMPTCPavailable() { // another error: MPTCP was not available but it might be later mptcpAvailable = true } + + major, minor := unix.KernelVersion() + // SOL_MPTCP only supported from kernel 5.16 + hasSOLMPTCP = major > 5 || (major == 5 && minor >= 16) } func (sd *sysDialer) dialMPTCP(ctx context.Context, laddr, raddr *TCPAddr) (*TCPConn, error) { @@ -74,3 +82,46 @@ func (sl *sysListener) listenMPTCP(ctx context.Context, laddr *TCPAddr) (*TCPLis // retry with "plain" TCP. return sl.listenTCP(ctx, laddr) } + +// hasFallenBack reports whether the MPTCP connection has fallen back to "plain" +// TCP. +// +// A connection can fallback to TCP for different reasons, e.g. the other peer +// doesn't support it, a middle box "accidentally" drops the option, etc. +// +// If the MPTCP protocol has not been requested when creating the socket, this +// method will return true: MPTCP is not being used. +// +// Kernel >= 5.16 returns EOPNOTSUPP/ENOPROTOOPT in case of fallback. +// Older kernels will always return them even if MPTCP is used: not usable. +func hasFallenBack(fd *netFD) bool { + _, err := fd.pfd.GetsockoptInt(_SOL_MPTCP, _MPTCP_INFO) + + // 2 expected errors in case of fallback depending on the address family + // - AF_INET: EOPNOTSUPP + // - AF_INET6: ENOPROTOOPT + return err == syscall.EOPNOTSUPP || err == syscall.ENOPROTOOPT +} + +// isUsingMPTCPProto reports whether the socket protocol is MPTCP. +// +// Compared to hasFallenBack method, here only the socket protocol being used is +// checked: it can be MPTCP but it doesn't mean MPTCP is used on the wire, maybe +// a fallback to TCP has been done. +func isUsingMPTCPProto(fd *netFD) bool { + proto, _ := fd.pfd.GetsockoptInt(syscall.SOL_SOCKET, syscall.SO_PROTOCOL) + + return proto == _IPPROTO_MPTCP +} + +// isUsingMultipathTCP reports whether MPTCP is still being used. +// +// Please look at the description of hasFallenBack (kernel >=5.16) and +// isUsingMPTCPProto methods for more details about what is being checked here. +func isUsingMultipathTCP(fd *netFD) bool { + if hasSOLMPTCP { + return !hasFallenBack(fd) + } + + return isUsingMPTCPProto(fd) +} |