gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/pkg/tcpip/transport/tcp/test/e2e/e2e.go (about)

     1  // Copyright 2022 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // Package e2e contains definitions common to all e2e tcp tests.
    16  package e2e
    17  
    18  import (
    19  	"bytes"
    20  	"testing"
    21  	"time"
    22  
    23  	"github.com/google/go-cmp/cmp"
    24  	"gvisor.dev/gvisor/pkg/tcpip"
    25  	"gvisor.dev/gvisor/pkg/tcpip/checker"
    26  	"gvisor.dev/gvisor/pkg/tcpip/header"
    27  	"gvisor.dev/gvisor/pkg/tcpip/seqnum"
    28  	"gvisor.dev/gvisor/pkg/tcpip/transport/tcp"
    29  	"gvisor.dev/gvisor/pkg/tcpip/transport/tcp/testing/context"
    30  	"gvisor.dev/gvisor/pkg/waiter"
    31  )
    32  
    33  const (
    34  	// DefaultMTU is the MTU, in bytes, used throughout the tests, except
    35  	// where another value is explicitly used. It is chosen to match the MTU
    36  	// of loopback interfaces on linux systems.
    37  	DefaultMTU = 65535
    38  
    39  	// DefaultIPv4MSS is the MSS sent by the network stack in SYN/SYN-ACK for an
    40  	// IPv4 endpoint when the MTU is set to defaultMTU in the test.
    41  	DefaultIPv4MSS = DefaultMTU - header.IPv4MinimumSize - header.TCPMinimumSize
    42  
    43  	// TSOptionSize is the size in bytes of the TCP timestamp option.
    44  	TSOptionSize = 12
    45  
    46  	// MaxTCPOptionSize is the maximum size TCP Options in a TCP header.
    47  	MaxTCPOptionSize = 40
    48  )
    49  
    50  // CheckBrokenUpWrite does a large write > than the specified maxPayload and
    51  // verifies that the received packets carry the expected payload and
    52  // that the large write was broken up into > 1 packet.
    53  func CheckBrokenUpWrite(t *testing.T, c *context.Context, maxPayload int) {
    54  	payloadMultiplier := 10
    55  	dataLen := payloadMultiplier * maxPayload
    56  	data := make([]byte, dataLen)
    57  	for i := range data {
    58  		data[i] = byte(i)
    59  	}
    60  
    61  	var r bytes.Reader
    62  	r.Reset(data)
    63  	if _, err := c.EP.Write(&r, tcpip.WriteOptions{}); err != nil {
    64  		t.Fatalf("Write failed: %s", err)
    65  	}
    66  
    67  	// Check that data is received in chunks.
    68  	bytesReceived := 0
    69  	numPackets := 0
    70  	iss := seqnum.Value(context.TestInitialSequenceNumber).Add(1)
    71  	for bytesReceived != dataLen {
    72  		v := c.GetPacket()
    73  		defer v.Release()
    74  		numPackets++
    75  		tcpHdr := header.TCP(header.IPv4(v.AsSlice()).Payload())
    76  		payloadLen := len(tcpHdr.Payload())
    77  		checker.IPv4(t, v,
    78  			checker.TCP(
    79  				checker.DstPort(context.TestPort),
    80  				checker.TCPSeqNum(uint32(c.IRS)+1+uint32(bytesReceived)),
    81  				checker.TCPAckNum(uint32(iss)),
    82  				checker.TCPFlagsMatch(header.TCPFlagAck, ^header.TCPFlagPsh),
    83  			),
    84  		)
    85  
    86  		pdata := data[bytesReceived : bytesReceived+payloadLen]
    87  		if p := tcpHdr.Payload(); !bytes.Equal(pdata, p) {
    88  			t.Fatalf("got data = %v, want = %v", p, pdata)
    89  		}
    90  		bytesReceived += payloadLen
    91  		var options []byte
    92  		if c.TimeStampEnabled {
    93  			// If timestamp option is enabled, echo back the timestamp and increment
    94  			// the TSEcr value included in the packet and send that back as the TSVal.
    95  			parsedOpts := tcpHdr.ParsedOptions()
    96  			tsOpt := [12]byte{header.TCPOptionNOP, header.TCPOptionNOP}
    97  			header.EncodeTSOption(parsedOpts.TSEcr+1, parsedOpts.TSVal, tsOpt[2:])
    98  			options = tsOpt[:]
    99  		}
   100  		// Acknowledge the data.
   101  		c.SendPacket(nil, &context.Headers{
   102  			SrcPort: context.TestPort,
   103  			DstPort: c.Port,
   104  			Flags:   header.TCPFlagAck,
   105  			SeqNum:  iss,
   106  			AckNum:  c.IRS.Add(1 + seqnum.Size(bytesReceived)),
   107  			RcvWnd:  30000,
   108  			TCPOpts: options,
   109  		})
   110  	}
   111  	if numPackets == 1 {
   112  		t.Fatalf("expected write to be broken up into multiple packets, but got 1 packet")
   113  	}
   114  }
   115  
   116  // CreateConnectedWithSACKPermittedOption creates and connects c.ep with the
   117  // SACKPermitted option enabled if the stack in the context has the SACK support
   118  // enabled.
   119  func CreateConnectedWithSACKPermittedOption(c *context.Context) *context.RawEndpoint {
   120  	return c.CreateConnectedWithOptionsNoDelay(header.TCPSynOptions{SACKPermitted: c.SACKEnabled()})
   121  }
   122  
   123  // CreateConnectedWithSACKAndTS creates and connects c.ep with the SACK & TS
   124  // option enabled if the stack in the context has SACK and TS enabled.
   125  func CreateConnectedWithSACKAndTS(c *context.Context) *context.RawEndpoint {
   126  	return c.CreateConnectedWithOptionsNoDelay(header.TCPSynOptions{SACKPermitted: c.SACKEnabled(), TS: true})
   127  }
   128  
   129  // SetStackSACKPermitted sets the tcpip.TCPSACKEnabled option of the context stack to
   130  // enabled value.
   131  func SetStackSACKPermitted(t *testing.T, c *context.Context, enable bool) {
   132  	t.Helper()
   133  	opt := tcpip.TCPSACKEnabled(enable)
   134  	if err := c.Stack().SetTransportProtocolOption(tcp.ProtocolNumber, &opt); err != nil {
   135  		t.Fatalf("c.s.SetTransportProtocolOption(%d, &%T(%t)): %s", tcp.ProtocolNumber, opt, opt, err)
   136  	}
   137  }
   138  
   139  // SetStackTCPRecovery sets the tcpip.TCPRecovery option of the context stack to
   140  // the specified recovery value.
   141  func SetStackTCPRecovery(t *testing.T, c *context.Context, recovery int) {
   142  	t.Helper()
   143  	opt := tcpip.TCPRecovery(recovery)
   144  	if err := c.Stack().SetTransportProtocolOption(header.TCPProtocolNumber, &opt); err != nil {
   145  		t.Fatalf("c.s.SetTransportProtocolOption(%d, &%v(%v)): %s", header.TCPProtocolNumber, opt, opt, err)
   146  	}
   147  }
   148  
   149  // SendAndReceiveWithSACK creates a SACK enabled connection w/ RACK enabled if
   150  // enableRACK is true. It then proceeds to write a large payload and verifies
   151  // that numPackets were received.
   152  func SendAndReceiveWithSACK(t *testing.T, c *context.Context, maxPayload int, numPackets int, enableRACK bool) []byte {
   153  	SetStackSACKPermitted(t, c, true)
   154  	if !enableRACK {
   155  		SetStackTCPRecovery(t, c, 0)
   156  	}
   157  	// The delay should be below initial RTO (1s) otherwise retransimission
   158  	// will start. Choose a relatively large value so that estimated RTT
   159  	// keeps high even after a few rounds of undelayed RTT samples.
   160  	c.CreateConnectedWithOptions(header.TCPSynOptions{SACKPermitted: c.SACKEnabled(), TS: true}, 800*time.Millisecond /* delay */)
   161  
   162  	data := make([]byte, numPackets*maxPayload)
   163  	for i := range data {
   164  		data[i] = byte(i)
   165  	}
   166  
   167  	// Write the data.
   168  	var r bytes.Reader
   169  	r.Reset(data)
   170  	if _, err := c.EP.Write(&r, tcpip.WriteOptions{}); err != nil {
   171  		t.Fatalf("Write failed: %s", err)
   172  	}
   173  
   174  	bytesRead := 0
   175  	for i := 0; i < numPackets; i++ {
   176  		c.ReceiveAndCheckPacketWithOptions(data, bytesRead, maxPayload, TSOptionSize)
   177  		bytesRead += maxPayload
   178  	}
   179  
   180  	return data
   181  }
   182  
   183  // EnableCUBIC sets the CUBIC congestion control as the default congestion
   184  // control algorithm for all newly created endpoints in the context stack.
   185  func EnableCUBIC(t *testing.T, c *context.Context) {
   186  	t.Helper()
   187  	opt := tcpip.CongestionControlOption("cubic")
   188  	if err := c.Stack().SetTransportProtocolOption(tcp.ProtocolNumber, &opt); err != nil {
   189  		t.Fatalf("SetTransportProtocolOption(%d, &%T(%s)) %s", tcp.ProtocolNumber, opt, opt, err)
   190  	}
   191  }
   192  
   193  // TestV4Connect establishes an IPv4 Connection with the context stack.
   194  func TestV4Connect(t *testing.T, c *context.Context, checkers ...checker.NetworkChecker) {
   195  	// Start connection attempt.
   196  	we, ch := waiter.NewChannelEntry(waiter.WritableEvents)
   197  	c.WQ.EventRegister(&we)
   198  	defer c.WQ.EventUnregister(&we)
   199  
   200  	err := c.EP.Connect(tcpip.FullAddress{Addr: context.TestV4MappedAddr, Port: context.TestPort})
   201  	if d := cmp.Diff(&tcpip.ErrConnectStarted{}, err); d != "" {
   202  		t.Fatalf("c.EP.Connect(...) mismatch (-want +got):\n%s", d)
   203  	}
   204  
   205  	// Receive SYN packet.
   206  	v := c.GetPacket()
   207  	defer v.Release()
   208  	synCheckers := append(checkers, checker.TCP(
   209  		checker.DstPort(context.TestPort),
   210  		checker.TCPFlags(header.TCPFlagSyn),
   211  	))
   212  	checker.IPv4(t, v, synCheckers...)
   213  
   214  	tcp := header.TCP(header.IPv4(v.AsSlice()).Payload())
   215  	c.IRS = seqnum.Value(tcp.SequenceNumber())
   216  
   217  	iss := seqnum.Value(789)
   218  	c.SendPacket(nil, &context.Headers{
   219  		SrcPort: tcp.DestinationPort(),
   220  		DstPort: tcp.SourcePort(),
   221  		Flags:   header.TCPFlagSyn | header.TCPFlagAck,
   222  		SeqNum:  iss,
   223  		AckNum:  c.IRS.Add(1),
   224  		RcvWnd:  30000,
   225  	})
   226  
   227  	// Receive ACK packet.
   228  	ackCheckers := append(checkers, checker.TCP(
   229  		checker.DstPort(context.TestPort),
   230  		checker.TCPFlags(header.TCPFlagAck),
   231  		checker.TCPSeqNum(uint32(c.IRS)+1),
   232  		checker.TCPAckNum(uint32(iss)+1),
   233  	))
   234  
   235  	v = c.GetPacket()
   236  	defer v.Release()
   237  	checker.IPv4(t, v, ackCheckers...)
   238  
   239  	// Wait for connection to be established.
   240  	select {
   241  	case <-ch:
   242  		if err := c.EP.LastError(); err != nil {
   243  			t.Fatalf("Unexpected error when connecting: %v", err)
   244  		}
   245  	case <-time.After(1 * time.Second):
   246  		t.Fatalf("Timed out waiting for connection")
   247  	}
   248  }
   249  
   250  // TestV6Connect establishes an IPv6 Connection with the context stack.
   251  func TestV6Connect(t *testing.T, c *context.Context, checkers ...checker.NetworkChecker) {
   252  	// Start connection attempt to IPv6 address.
   253  	we, ch := waiter.NewChannelEntry(waiter.WritableEvents)
   254  	c.WQ.EventRegister(&we)
   255  	defer c.WQ.EventUnregister(&we)
   256  
   257  	err := c.EP.Connect(tcpip.FullAddress{Addr: context.TestV6Addr, Port: context.TestPort})
   258  	if d := cmp.Diff(&tcpip.ErrConnectStarted{}, err); d != "" {
   259  		t.Fatalf("Connect(...) mismatch (-want +got):\n%s", d)
   260  	}
   261  
   262  	// Receive SYN packet.
   263  	v := c.GetV6Packet()
   264  	defer v.Release()
   265  	synCheckers := append(checkers, checker.TCP(
   266  		checker.DstPort(context.TestPort),
   267  		checker.TCPFlags(header.TCPFlagSyn),
   268  	))
   269  	checker.IPv6(t, v, synCheckers...)
   270  
   271  	tcp := header.TCP(header.IPv6(v.AsSlice()).Payload())
   272  	c.IRS = seqnum.Value(tcp.SequenceNumber())
   273  
   274  	iss := seqnum.Value(789)
   275  	c.SendV6Packet(nil, &context.Headers{
   276  		SrcPort: tcp.DestinationPort(),
   277  		DstPort: tcp.SourcePort(),
   278  		Flags:   header.TCPFlagSyn | header.TCPFlagAck,
   279  		SeqNum:  iss,
   280  		AckNum:  c.IRS.Add(1),
   281  		RcvWnd:  30000,
   282  	})
   283  
   284  	// Receive ACK packet.
   285  	ackCheckers := append(checkers, checker.TCP(
   286  		checker.DstPort(context.TestPort),
   287  		checker.TCPFlags(header.TCPFlagAck),
   288  		checker.TCPSeqNum(uint32(c.IRS)+1),
   289  		checker.TCPAckNum(uint32(iss)+1),
   290  	))
   291  	v = c.GetV6Packet()
   292  	defer v.Release()
   293  	checker.IPv6(t, v, ackCheckers...)
   294  
   295  	// Wait for connection to be established.
   296  	select {
   297  	case <-ch:
   298  		if err := c.EP.LastError(); err != nil {
   299  			t.Fatalf("Unexpected error when connecting: %v", err)
   300  		}
   301  	case <-time.After(1 * time.Second):
   302  		t.Fatalf("Timed out waiting for connection")
   303  	}
   304  }