| // 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) |
| } |
| } |