Source file src/net/mptcpsock_linux_test.go

     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.
     4  
     5  package net
     6  
     7  import (
     8  	"bytes"
     9  	"context"
    10  	"errors"
    11  	"syscall"
    12  	"testing"
    13  )
    14  
    15  func newLocalListenerMPTCP(t *testing.T, envVar bool) Listener {
    16  	lc := &ListenConfig{}
    17  
    18  	if envVar {
    19  		if !lc.MultipathTCP() {
    20  			t.Fatal("MultipathTCP Listen is not on despite GODEBUG=multipathtcp=1")
    21  		}
    22  	} else {
    23  		if lc.MultipathTCP() {
    24  			t.Error("MultipathTCP should be off by default")
    25  		}
    26  
    27  		lc.SetMultipathTCP(true)
    28  		if !lc.MultipathTCP() {
    29  			t.Fatal("MultipathTCP is not on after having been forced to on")
    30  		}
    31  	}
    32  
    33  	ln, err := lc.Listen(context.Background(), "tcp", "127.0.0.1:0")
    34  	if err != nil {
    35  		t.Fatal(err)
    36  	}
    37  	return ln
    38  }
    39  
    40  func postAcceptMPTCP(ls *localServer, ch chan<- error) {
    41  	defer close(ch)
    42  
    43  	if len(ls.cl) == 0 {
    44  		ch <- errors.New("no accepted stream")
    45  		return
    46  	}
    47  
    48  	c := ls.cl[0]
    49  
    50  	tcp, ok := c.(*TCPConn)
    51  	if !ok {
    52  		ch <- errors.New("struct is not a TCPConn")
    53  		return
    54  	}
    55  
    56  	mptcp, err := tcp.MultipathTCP()
    57  	if err != nil {
    58  		ch <- err
    59  		return
    60  	}
    61  
    62  	if !mptcp {
    63  		ch <- errors.New("incoming connection is not with MPTCP")
    64  		return
    65  	}
    66  
    67  	// Also check the method for the older kernels if not tested before
    68  	if hasSOLMPTCP && !isUsingMPTCPProto(tcp.fd) {
    69  		ch <- errors.New("incoming connection is not an MPTCP proto")
    70  		return
    71  	}
    72  }
    73  
    74  func dialerMPTCP(t *testing.T, addr string, envVar bool) {
    75  	d := &Dialer{}
    76  
    77  	if envVar {
    78  		if !d.MultipathTCP() {
    79  			t.Fatal("MultipathTCP Dialer is not on despite GODEBUG=multipathtcp=1")
    80  		}
    81  	} else {
    82  		if d.MultipathTCP() {
    83  			t.Error("MultipathTCP should be off by default")
    84  		}
    85  
    86  		d.SetMultipathTCP(true)
    87  		if !d.MultipathTCP() {
    88  			t.Fatal("MultipathTCP is not on after having been forced to on")
    89  		}
    90  	}
    91  
    92  	c, err := d.Dial("tcp", addr)
    93  	if err != nil {
    94  		t.Fatal(err)
    95  	}
    96  	defer c.Close()
    97  
    98  	tcp, ok := c.(*TCPConn)
    99  	if !ok {
   100  		t.Fatal("struct is not a TCPConn")
   101  	}
   102  
   103  	// Transfer a bit of data to make sure everything is still OK
   104  	snt := []byte("MPTCP TEST")
   105  	if _, err := c.Write(snt); err != nil {
   106  		t.Fatal(err)
   107  	}
   108  	b := make([]byte, len(snt))
   109  	if _, err := c.Read(b); err != nil {
   110  		t.Fatal(err)
   111  	}
   112  	if !bytes.Equal(snt, b) {
   113  		t.Errorf("sent bytes (%s) are different from received ones (%s)", snt, b)
   114  	}
   115  
   116  	mptcp, err := tcp.MultipathTCP()
   117  	if err != nil {
   118  		t.Fatal(err)
   119  	}
   120  
   121  	t.Logf("outgoing connection from %s with mptcp: %t", addr, mptcp)
   122  
   123  	if !mptcp {
   124  		t.Error("outgoing connection is not with MPTCP")
   125  	}
   126  
   127  	// Also check the method for the older kernels if not tested before
   128  	if hasSOLMPTCP && !isUsingMPTCPProto(tcp.fd) {
   129  		t.Error("outgoing connection is not an MPTCP proto")
   130  	}
   131  }
   132  
   133  func canCreateMPTCPSocket() bool {
   134  	// We want to know if we can create an MPTCP socket, not just if it is
   135  	// available (mptcpAvailable()): it could be blocked by the admin
   136  	fd, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_STREAM, _IPPROTO_MPTCP)
   137  	if err != nil {
   138  		return false
   139  	}
   140  
   141  	syscall.Close(fd)
   142  	return true
   143  }
   144  
   145  func testMultiPathTCP(t *testing.T, envVar bool) {
   146  	if envVar {
   147  		t.Log("Test with GODEBUG=multipathtcp=1")
   148  		t.Setenv("GODEBUG", "multipathtcp=1")
   149  	} else {
   150  		t.Log("Test with GODEBUG=multipathtcp=0")
   151  		t.Setenv("GODEBUG", "multipathtcp=0")
   152  	}
   153  
   154  	ln := newLocalListenerMPTCP(t, envVar)
   155  
   156  	// similar to tcpsock_test:TestIPv6LinkLocalUnicastTCP
   157  	ls := (&streamListener{Listener: ln}).newLocalServer()
   158  	defer ls.teardown()
   159  
   160  	if g, w := ls.Listener.Addr().Network(), "tcp"; g != w {
   161  		t.Fatalf("Network type mismatch: got %q, want %q", g, w)
   162  	}
   163  
   164  	genericCh := make(chan error)
   165  	mptcpCh := make(chan error)
   166  	handler := func(ls *localServer, ln Listener) {
   167  		ls.transponder(ln, genericCh)
   168  		postAcceptMPTCP(ls, mptcpCh)
   169  	}
   170  	if err := ls.buildup(handler); err != nil {
   171  		t.Fatal(err)
   172  	}
   173  
   174  	dialerMPTCP(t, ln.Addr().String(), envVar)
   175  
   176  	if err := <-genericCh; err != nil {
   177  		t.Error(err)
   178  	}
   179  	if err := <-mptcpCh; err != nil {
   180  		t.Error(err)
   181  	}
   182  }
   183  
   184  func TestMultiPathTCP(t *testing.T) {
   185  	if !canCreateMPTCPSocket() {
   186  		t.Skip("Cannot create MPTCP sockets")
   187  	}
   188  
   189  	for _, envVar := range []bool{false, true} {
   190  		testMultiPathTCP(t, envVar)
   191  	}
   192  }
   193  

View as plain text