1 // Copyright 2023 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
20 // These constants aren't in the syscall package, which is frozen
22 _IPPROTO_MPTCP = 0x106
25 func supportsMultipathTCP() bool {
26 mptcpOnce.Do(initMPTCPavailable)
30 // Check that MPTCP is supported by attemting to create an MPTCP socket and by
31 // looking at the returned error if any.
32 func initMPTCPavailable() {
33 s, err := sysSocket(syscall.AF_INET, syscall.SOCK_STREAM, _IPPROTO_MPTCP)
35 case errors.Is(err, syscall.EPROTONOSUPPORT): // Not supported: >= v5.6
36 case errors.Is(err, syscall.EINVAL): // Not supported: < v5.6
37 case err == nil: // Supported and no error
41 // another error: MPTCP was not available but it might be later
46 func (sd *sysDialer) dialMPTCP(ctx context.Context, laddr, raddr *TCPAddr) (*TCPConn, error) {
47 if supportsMultipathTCP() {
48 if conn, err := sd.doDialTCPProto(ctx, laddr, raddr, _IPPROTO_MPTCP); err == nil {
53 // Fallback to dialTCP if Multipath TCP isn't supported on this operating
54 // system. But also fallback in case of any error with MPTCP.
56 // Possible MPTCP specific error: ENOPROTOOPT (sysctl net.mptcp.enabled=0)
57 // But just in case MPTCP is blocked differently (SELinux, etc.), just
58 // retry with "plain" TCP.
59 return sd.dialTCP(ctx, laddr, raddr)
62 func (sl *sysListener) listenMPTCP(ctx context.Context, laddr *TCPAddr) (*TCPListener, error) {
63 if supportsMultipathTCP() {
64 if dial, err := sl.listenTCPProto(ctx, laddr, _IPPROTO_MPTCP); err == nil {
69 // Fallback to listenTCP if Multipath TCP isn't supported on this operating
70 // system. But also fallback in case of any error with MPTCP.
72 // Possible MPTCP specific error: ENOPROTOOPT (sysctl net.mptcp.enabled=0)
73 // But just in case MPTCP is blocked differently (SELinux, etc.), just
74 // retry with "plain" TCP.
75 return sl.listenTCP(ctx, laddr)