mirror of https://go.googlesource.com/go
193 lines
4.1 KiB
Go
193 lines
4.1 KiB
Go
// 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 (
|
|
"bytes"
|
|
"context"
|
|
"errors"
|
|
"syscall"
|
|
"testing"
|
|
)
|
|
|
|
func newLocalListenerMPTCP(t *testing.T, envVar bool) Listener {
|
|
lc := &ListenConfig{}
|
|
|
|
if envVar {
|
|
if !lc.MultipathTCP() {
|
|
t.Fatal("MultipathTCP Listen is not on despite GODEBUG=multipathtcp=1")
|
|
}
|
|
} else {
|
|
if lc.MultipathTCP() {
|
|
t.Error("MultipathTCP should be off by default")
|
|
}
|
|
|
|
lc.SetMultipathTCP(true)
|
|
if !lc.MultipathTCP() {
|
|
t.Fatal("MultipathTCP is not on after having been forced to on")
|
|
}
|
|
}
|
|
|
|
ln, err := lc.Listen(context.Background(), "tcp", "127.0.0.1:0")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return ln
|
|
}
|
|
|
|
func postAcceptMPTCP(ls *localServer, ch chan<- error) {
|
|
defer close(ch)
|
|
|
|
if len(ls.cl) == 0 {
|
|
ch <- errors.New("no accepted stream")
|
|
return
|
|
}
|
|
|
|
c := ls.cl[0]
|
|
|
|
tcp, ok := c.(*TCPConn)
|
|
if !ok {
|
|
ch <- errors.New("struct is not a TCPConn")
|
|
return
|
|
}
|
|
|
|
mptcp, err := tcp.MultipathTCP()
|
|
if err != nil {
|
|
ch <- err
|
|
return
|
|
}
|
|
|
|
if !mptcp {
|
|
ch <- errors.New("incoming connection is not with MPTCP")
|
|
return
|
|
}
|
|
|
|
// Also check the method for the older kernels if not tested before
|
|
if hasSOLMPTCP && !isUsingMPTCPProto(tcp.fd) {
|
|
ch <- errors.New("incoming connection is not an MPTCP proto")
|
|
return
|
|
}
|
|
}
|
|
|
|
func dialerMPTCP(t *testing.T, addr string, envVar bool) {
|
|
d := &Dialer{}
|
|
|
|
if envVar {
|
|
if !d.MultipathTCP() {
|
|
t.Fatal("MultipathTCP Dialer is not on despite GODEBUG=multipathtcp=1")
|
|
}
|
|
} else {
|
|
if d.MultipathTCP() {
|
|
t.Error("MultipathTCP should be off by default")
|
|
}
|
|
|
|
d.SetMultipathTCP(true)
|
|
if !d.MultipathTCP() {
|
|
t.Fatal("MultipathTCP is not on after having been forced to on")
|
|
}
|
|
}
|
|
|
|
c, err := d.Dial("tcp", addr)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer c.Close()
|
|
|
|
tcp, ok := c.(*TCPConn)
|
|
if !ok {
|
|
t.Fatal("struct is not a TCPConn")
|
|
}
|
|
|
|
// Transfer a bit of data to make sure everything is still OK
|
|
snt := []byte("MPTCP TEST")
|
|
if _, err := c.Write(snt); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
b := make([]byte, len(snt))
|
|
if _, err := c.Read(b); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !bytes.Equal(snt, b) {
|
|
t.Errorf("sent bytes (%s) are different from received ones (%s)", snt, b)
|
|
}
|
|
|
|
mptcp, err := tcp.MultipathTCP()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
t.Logf("outgoing connection from %s with mptcp: %t", addr, mptcp)
|
|
|
|
if !mptcp {
|
|
t.Error("outgoing connection is not with MPTCP")
|
|
}
|
|
|
|
// Also check the method for the older kernels if not tested before
|
|
if hasSOLMPTCP && !isUsingMPTCPProto(tcp.fd) {
|
|
t.Error("outgoing connection is not an MPTCP proto")
|
|
}
|
|
}
|
|
|
|
func canCreateMPTCPSocket() bool {
|
|
// We want to know if we can create an MPTCP socket, not just if it is
|
|
// available (mptcpAvailable()): it could be blocked by the admin
|
|
fd, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_STREAM, _IPPROTO_MPTCP)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
|
|
syscall.Close(fd)
|
|
return true
|
|
}
|
|
|
|
func testMultiPathTCP(t *testing.T, envVar bool) {
|
|
if envVar {
|
|
t.Log("Test with GODEBUG=multipathtcp=1")
|
|
t.Setenv("GODEBUG", "multipathtcp=1")
|
|
} else {
|
|
t.Log("Test with GODEBUG=multipathtcp=0")
|
|
t.Setenv("GODEBUG", "multipathtcp=0")
|
|
}
|
|
|
|
ln := newLocalListenerMPTCP(t, envVar)
|
|
|
|
// similar to tcpsock_test:TestIPv6LinkLocalUnicastTCP
|
|
ls := (&streamListener{Listener: ln}).newLocalServer()
|
|
defer ls.teardown()
|
|
|
|
if g, w := ls.Listener.Addr().Network(), "tcp"; g != w {
|
|
t.Fatalf("Network type mismatch: got %q, want %q", g, w)
|
|
}
|
|
|
|
genericCh := make(chan error)
|
|
mptcpCh := make(chan error)
|
|
handler := func(ls *localServer, ln Listener) {
|
|
ls.transponder(ln, genericCh)
|
|
postAcceptMPTCP(ls, mptcpCh)
|
|
}
|
|
if err := ls.buildup(handler); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
dialerMPTCP(t, ln.Addr().String(), envVar)
|
|
|
|
if err := <-genericCh; err != nil {
|
|
t.Error(err)
|
|
}
|
|
if err := <-mptcpCh; err != nil {
|
|
t.Error(err)
|
|
}
|
|
}
|
|
|
|
func TestMultiPathTCP(t *testing.T) {
|
|
if !canCreateMPTCPSocket() {
|
|
t.Skip("Cannot create MPTCP sockets")
|
|
}
|
|
|
|
for _, envVar := range []bool{false, true} {
|
|
testMultiPathTCP(t, envVar)
|
|
}
|
|
}
|