From 383a4e78861218bd861632d6cdcac6c8af6a5470 Mon Sep 17 00:00:00 2001 From: Matthieu Baerts Date: Fri, 24 Feb 2023 17:52:01 +0100 Subject: [PATCH] net: mptcp: add end-to-end test 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 and Gregory Detal . Fixes #56539 Change-Id: I4b6b39e9175a20f98497b5ea56934e242da06194 Reviewed-on: https://go-review.googlesource.com/c/go/+/471141 Reviewed-by: Damien Neil Reviewed-by: Ian Lance Taylor Run-TryBot: Emmanuel Odeke TryBot-Result: Gopher Robot Reviewed-by: Emmanuel Odeke Auto-Submit: Emmanuel Odeke --- src/net/mptcpsock_linux_test.go | 133 ++++++++++++++++++++++++++++++++ 1 file changed, 133 insertions(+) create mode 100644 src/net/mptcpsock_linux_test.go diff --git a/src/net/mptcpsock_linux_test.go b/src/net/mptcpsock_linux_test.go new file mode 100644 index 0000000000..11543b0c8c --- /dev/null +++ b/src/net/mptcpsock_linux_test.go @@ -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) + } +} -- 2.50.0