]> Cypherpunks repositories - gostls13.git/commitdiff
net: mptcp: add end-to-end test
authorMatthieu Baerts <matthieu.baerts@tessares.net>
Fri, 24 Feb 2023 16:52:01 +0000 (17:52 +0100)
committerGopher Robot <gobot@golang.org>
Thu, 30 Mar 2023 14:27:38 +0000 (14:27 +0000)
This adds a simple test validating MPTCP Sock for Linux implementation:

- A Listener is created with MPTCP support, accepting new connections in
  a new thread.

- A Dialer with MPTCP support connects to this new Listener

- On both sides, MPTCP should be used. Note that at this point, we
  cannot check if a fallback to TCP has been done nor if the correct
  protocol is being used.

Technically, a localServer from mockserver_test.go is used, similar to
TestIPv6LinkLocalUnicastTCP from tcpsock_test.go. Here with MPTCP, the
Listen step is done manually to force using MPTCP and a post step is
done to verify extra status after the Accept. More checks are going to
be done in the future.

Please note that the test is skipped if the kernel doesn't allow the
creation of an MPTCP socket at all when starting the test.

The test can be executed with this command:

  $ ../bin/go test -v net -run "^TestMultiPathTCP$"

The "-race" option has also been checked.

This work has been co-developped by Benjamin Hesmans
<benjamin.hesmans@tessares.net> and Gregory Detal
<gregory.detal@tessares.net>.

Fixes #56539

Change-Id: I4b6b39e9175a20f98497b5ea56934e242da06194
Reviewed-on: https://go-review.googlesource.com/c/go/+/471141
Reviewed-by: Damien Neil <dneil@google.com>
Reviewed-by: Ian Lance Taylor <iant@google.com>
Run-TryBot: Emmanuel Odeke <emmanuel@orijtech.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Emmanuel Odeke <emmanuel@orijtech.com>
Auto-Submit: Emmanuel Odeke <emmanuel@orijtech.com>

src/net/mptcpsock_linux_test.go [new file with mode: 0644]

diff --git a/src/net/mptcpsock_linux_test.go b/src/net/mptcpsock_linux_test.go
new file mode 100644 (file)
index 0000000..11543b0
--- /dev/null
@@ -0,0 +1,133 @@
+// 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) Listener {
+       lc := &ListenConfig{}
+       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]
+
+       _, ok := c.(*TCPConn)
+       if !ok {
+               ch <- errors.New("struct is not a TCPConn")
+               return
+       }
+}
+
+func dialerMPTCP(t *testing.T, addr string) {
+       d := &Dialer{}
+       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()
+
+       _, 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)
+       }
+
+       t.Logf("outgoing connection from %s with mptcp", addr)
+}
+
+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) {
+       if !canCreateMPTCPSocket() {
+               t.Skip("Cannot create MPTCP sockets")
+       }
+
+       ln := newLocalListenerMPTCP(t)
+
+       // 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())
+
+       if err := <-genericCh; err != nil {
+               t.Error(err)
+       }
+       if err := <-mptcpCh; err != nil {
+               t.Error(err)
+       }
+}