net: mptcp: implement dialMPTCP

This function is called when the user has requested MPTCP via
SetMultipathTCP in the Dialer.

This new function falls back to dialTCP on operating systems that do not
support MPTCP or if MPTCP is not supported.

On Dialer side, MultipathTCP function can be used to know if the package
will try to use MPTCP or not when Dial is called.

Note that this new dialMPTCP function returns a TCPConn object, like
dialTCP. A new MPTCPConn object using the following composition could
have been returned:

    type MPTCPConn struct {
        *TCPConn
    }

But the drawback is that if MPTCP is used by default one day (see #56539
issue on GitHub), Dial will return a different object: this new
MPTCPConn type instead of the previously expected TCPConn. This can
cause issues for apps checking the returned object.

This work has been co-developped by Gregory Detal
<gregory.detal@tessares.net>.

Updates #56539

Change-Id: I0f9b5b81f630b39142bdd553d4f1b4c775f1dff0
Reviewed-on: https://go-review.googlesource.com/c/go/+/471136
Reviewed-by: Ian Lance Taylor <iant@google.com>
Auto-Submit: Ian Lance Taylor <iant@google.com>
Run-TryBot: Emmanuel Odeke <emmanuel@orijtech.com>
Reviewed-by: Emmanuel Odeke <emmanuel@orijtech.com>
Run-TryBot: Ian Lance Taylor <iant@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
This commit is contained in:
Matthieu Baerts 2023-02-24 17:51:57 +01:00 committed by Gopher Robot
parent 6615324f79
commit 0bd94e4387
5 changed files with 54 additions and 3 deletions

2
api/next/56539.txt Normal file
View file

@ -0,0 +1,2 @@
pkg net, method (*Dialer) MultipathTCP() bool #56539
pkg net, method (*Dialer) SetMultipathTCP(bool) #56539

View file

@ -141,6 +141,11 @@ type Dialer struct {
//
// If ControlContext is not nil, Control is ignored.
ControlContext func(ctx context.Context, network, address string, c syscall.RawConn) error
// If mptcpStatus is set to a value allowing Multipath TCP (MPTCP) to be
// used, any call to Dial with "tcp(4|6)" as network will use MPTCP if
// supported by the operating system.
mptcpStatus mptcpStatus
}
func (d *Dialer) dualStack() bool { return d.FallbackDelay >= 0 }
@ -314,6 +319,24 @@ func (r *Resolver) resolveAddrList(ctx context.Context, op, network, addr string
return naddrs, nil
}
// MultipathTCP reports whether MPTCP will be used.
//
// This method doesn't check if MPTCP is supported by the operating
// system or not.
func (d *Dialer) MultipathTCP() bool {
return d.mptcpStatus.get()
}
// SetMultipathTCP directs the Dial methods to use, or not use, MPTCP,
// if supported by the operating system. This method overrides the
// system default.
//
// If MPTCP is not available on the host or not supported by the server,
// the Dial methods will fall back to TCP.
func (d *Dialer) SetMultipathTCP(use bool) {
d.mptcpStatus.set(use)
}
// Dial connects to the address on the named network.
//
// Known networks are "tcp", "tcp4" (IPv4-only), "tcp6" (IPv6-only),
@ -610,7 +633,11 @@ func (sd *sysDialer) dialSingle(ctx context.Context, ra Addr) (c Conn, err error
switch ra := ra.(type) {
case *TCPAddr:
la, _ := la.(*TCPAddr)
c, err = sd.dialTCP(ctx, la, ra)
if sd.MultipathTCP() {
c, err = sd.dialMPTCP(ctx, la, ra)
} else {
c, err = sd.dialTCP(ctx, la, ra)
}
case *UDPAddr:
la, _ := la.(*UDPAddr)
c, err = sd.dialUDP(ctx, la, ra)

View file

@ -5,6 +5,7 @@
package net
import (
"context"
"errors"
"internal/poll"
"sync"
@ -41,3 +42,12 @@ func initMPTCPavailable() {
mptcpAvailable = true
}
}
func (sd *sysDialer) dialMPTCP(ctx context.Context, laddr, raddr *TCPAddr) (*TCPConn, error) {
// Fallback to dialTCP if Multipath TCP isn't supported on this operating system.
if !supportsMultipathTCP() {
return sd.dialTCP(ctx, laddr, raddr)
}
return sd.doDialTCPProto(ctx, laddr, raddr, _IPPROTO_MPTCP)
}

View file

@ -5,3 +5,11 @@
//go:build !linux
package net
import (
"context"
)
func (sd *sysDialer) dialMPTCP(ctx context.Context, laddr, raddr *TCPAddr) (*TCPConn, error) {
return sd.dialTCP(ctx, laddr, raddr)
}

View file

@ -65,13 +65,17 @@ func (sd *sysDialer) dialTCP(ctx context.Context, laddr, raddr *TCPAddr) (*TCPCo
}
func (sd *sysDialer) doDialTCP(ctx context.Context, laddr, raddr *TCPAddr) (*TCPConn, error) {
return sd.doDialTCPProto(ctx, laddr, raddr, 0)
}
func (sd *sysDialer) doDialTCPProto(ctx context.Context, laddr, raddr *TCPAddr, proto int) (*TCPConn, error) {
ctrlCtxFn := sd.Dialer.ControlContext
if ctrlCtxFn == nil && sd.Dialer.Control != nil {
ctrlCtxFn = func(cxt context.Context, network, address string, c syscall.RawConn) error {
return sd.Dialer.Control(network, address, c)
}
}
fd, err := internetSocket(ctx, sd.network, laddr, raddr, syscall.SOCK_STREAM, 0, "dial", ctrlCtxFn)
fd, err := internetSocket(ctx, sd.network, laddr, raddr, syscall.SOCK_STREAM, proto, "dial", ctrlCtxFn)
// TCP has a rarely used mechanism called a 'simultaneous connection' in
// which Dial("tcp", addr1, addr2) run on the machine at addr1 can
@ -101,7 +105,7 @@ func (sd *sysDialer) doDialTCP(ctx context.Context, laddr, raddr *TCPAddr) (*TCP
if err == nil {
fd.Close()
}
fd, err = internetSocket(ctx, sd.network, laddr, raddr, syscall.SOCK_STREAM, 0, "dial", ctrlCtxFn)
fd, err = internetSocket(ctx, sd.network, laddr, raddr, syscall.SOCK_STREAM, proto, "dial", ctrlCtxFn)
}
if err != nil {