blob: 15a788249890af812ccef966314f1c285e9f2551 [file] [log] [blame]
// Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package net
import (
"context"
"errors"
"internal/poll"
"sync"
"syscall"
)
var (
mptcpOnce sync.Once
mptcpAvailable bool
)
// These constants aren't in the syscall package, which is frozen
const (
_IPPROTO_MPTCP = 0x106
)
func supportsMultipathTCP() bool {
mptcpOnce.Do(initMPTCPavailable)
return mptcpAvailable
}
// Check that MPTCP is supported by attemting to create an MPTCP socket and by
// looking at the returned error if any.
func initMPTCPavailable() {
s, err := sysSocket(syscall.AF_INET, syscall.SOCK_STREAM, _IPPROTO_MPTCP)
switch {
case errors.Is(err, syscall.EPROTONOSUPPORT): // Not supported: >= v5.6
case errors.Is(err, syscall.EINVAL): // Not supported: < v5.6
case err == nil: // Supported and no error
poll.CloseFunc(s)
fallthrough
default:
// another error: MPTCP was not available but it might be later
mptcpAvailable = true
}
}
func (sd *sysDialer) dialMPTCP(ctx context.Context, laddr, raddr *TCPAddr) (*TCPConn, error) {
if supportsMultipathTCP() {
if conn, err := sd.doDialTCPProto(ctx, laddr, raddr, _IPPROTO_MPTCP); err == nil {
return conn, nil
}
}
// Fallback to dialTCP if Multipath TCP isn't supported on this operating
// system. But also fallback in case of any error with MPTCP.
//
// Possible MPTCP specific error: ENOPROTOOPT (sysctl net.mptcp.enabled=0)
// But just in case MPTCP is blocked differently (SELinux, etc.), just
// retry with "plain" TCP.
return sd.dialTCP(ctx, laddr, raddr)
}
func (sl *sysListener) listenMPTCP(ctx context.Context, laddr *TCPAddr) (*TCPListener, error) {
if supportsMultipathTCP() {
if dial, err := sl.listenTCPProto(ctx, laddr, _IPPROTO_MPTCP); err == nil {
return dial, nil
}
}
// Fallback to listenTCP if Multipath TCP isn't supported on this operating
// system. But also fallback in case of any error with MPTCP.
//
// Possible MPTCP specific error: ENOPROTOOPT (sysctl net.mptcp.enabled=0)
// But just in case MPTCP is blocked differently (SELinux, etc.), just
// retry with "plain" TCP.
return sl.listenTCP(ctx, laddr)
}