gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/test/packetimpact/tests/tcp_unacc_seq_ack_test.go (about)

     1  // Copyright 2020 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 tcp_unacc_seq_ack_test
    16  
    17  import (
    18  	"flag"
    19  	"fmt"
    20  	"testing"
    21  	"time"
    22  
    23  	"golang.org/x/sys/unix"
    24  	"gvisor.dev/gvisor/pkg/tcpip/header"
    25  	"gvisor.dev/gvisor/pkg/tcpip/seqnum"
    26  	"gvisor.dev/gvisor/test/packetimpact/testbench"
    27  )
    28  
    29  func init() {
    30  	testbench.Initialize(flag.CommandLine)
    31  }
    32  
    33  func TestEstablishedUnaccSeqAck(t *testing.T) {
    34  	for _, tt := range []struct {
    35  		description    string
    36  		makeTestingTCP func(t *testing.T, conn *testbench.TCPIPv4, seqNumOffset, windowSize seqnum.Size) testbench.TCP
    37  		seqNumOffset   seqnum.Size
    38  		expectAck      bool
    39  		restoreSeq     bool
    40  	}{
    41  		{description: "OTWSeq", makeTestingTCP: testbench.GenerateOTWSeqSegment, seqNumOffset: 0, expectAck: true, restoreSeq: true},
    42  		{description: "OTWSeq", makeTestingTCP: testbench.GenerateOTWSeqSegment, seqNumOffset: 1, expectAck: true, restoreSeq: true},
    43  		{description: "OTWSeq", makeTestingTCP: testbench.GenerateOTWSeqSegment, seqNumOffset: 2, expectAck: true, restoreSeq: true},
    44  		{description: "UnaccAck", makeTestingTCP: testbench.GenerateUnaccACKSegment, seqNumOffset: 0, expectAck: true, restoreSeq: false},
    45  		{description: "UnaccAck", makeTestingTCP: testbench.GenerateUnaccACKSegment, seqNumOffset: 1, expectAck: false, restoreSeq: true},
    46  		{description: "UnaccAck", makeTestingTCP: testbench.GenerateUnaccACKSegment, seqNumOffset: 2, expectAck: false, restoreSeq: true},
    47  	} {
    48  		t.Run(fmt.Sprintf("%s:offset=%d", tt.description, tt.seqNumOffset), func(t *testing.T) {
    49  			dut := testbench.NewDUT(t)
    50  			listenFD, remotePort := dut.CreateListener(t, unix.SOCK_STREAM, unix.IPPROTO_TCP, 1 /*backlog*/)
    51  			defer dut.Close(t, listenFD)
    52  			conn := dut.Net.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort})
    53  			defer conn.Close(t)
    54  
    55  			conn.Connect(t)
    56  			dut.Accept(t, listenFD)
    57  
    58  			sampleData := []byte("Sample Data")
    59  			samplePayload := &testbench.Payload{Bytes: sampleData}
    60  
    61  			conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck | header.TCPFlagPsh)}, samplePayload)
    62  			gotTCP, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)}, time.Second)
    63  			if err != nil {
    64  				t.Fatalf("expected ack %s", err)
    65  			}
    66  			windowSize := seqnum.Size(*gotTCP.WindowSize)
    67  
    68  			origSeq := *conn.LocalSeqNum(t)
    69  			// Send a segment with OTW Seq / unacc ACK.
    70  			conn.Send(t, tt.makeTestingTCP(t, &conn, tt.seqNumOffset, windowSize), samplePayload)
    71  			if tt.restoreSeq {
    72  				// Restore the local sequence number to ensure that the incoming
    73  				// ACK matches the TCP layer state.
    74  				*conn.LocalSeqNum(t) = origSeq
    75  			}
    76  			gotAck, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)}, time.Second)
    77  			if tt.expectAck && err != nil {
    78  				t.Fatalf("expected an ack but got none: %s", err)
    79  			}
    80  			if err == nil && !tt.expectAck && gotAck != nil {
    81  				t.Fatalf("expected no ack but got one: %s", gotAck)
    82  			}
    83  		})
    84  	}
    85  }
    86  
    87  func TestPassiveCloseUnaccSeqAck(t *testing.T) {
    88  	for _, tt := range []struct {
    89  		description    string
    90  		makeTestingTCP func(t *testing.T, conn *testbench.TCPIPv4, seqNumOffset, windowSize seqnum.Size) testbench.TCP
    91  		seqNumOffset   seqnum.Size
    92  		expectAck      bool
    93  	}{
    94  		{description: "OTWSeq", makeTestingTCP: testbench.GenerateOTWSeqSegment, seqNumOffset: 0, expectAck: false},
    95  		{description: "OTWSeq", makeTestingTCP: testbench.GenerateOTWSeqSegment, seqNumOffset: 1, expectAck: true},
    96  		{description: "OTWSeq", makeTestingTCP: testbench.GenerateOTWSeqSegment, seqNumOffset: 2, expectAck: true},
    97  		{description: "UnaccAck", makeTestingTCP: testbench.GenerateUnaccACKSegment, seqNumOffset: 0, expectAck: false},
    98  		{description: "UnaccAck", makeTestingTCP: testbench.GenerateUnaccACKSegment, seqNumOffset: 1, expectAck: true},
    99  		{description: "UnaccAck", makeTestingTCP: testbench.GenerateUnaccACKSegment, seqNumOffset: 2, expectAck: true},
   100  	} {
   101  		t.Run(fmt.Sprintf("%s:offset=%d", tt.description, tt.seqNumOffset), func(t *testing.T) {
   102  			dut := testbench.NewDUT(t)
   103  			listenFD, remotePort := dut.CreateListener(t, unix.SOCK_STREAM, unix.IPPROTO_TCP, 1 /*backlog*/)
   104  			defer dut.Close(t, listenFD)
   105  			conn := dut.Net.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort})
   106  			defer conn.Close(t)
   107  
   108  			conn.Connect(t)
   109  			acceptFD, _ := dut.Accept(t, listenFD)
   110  
   111  			// Send a FIN to DUT to intiate the passive close.
   112  			conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck | header.TCPFlagFin)})
   113  			gotTCP, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)}, time.Second)
   114  			if err != nil {
   115  				t.Fatalf("expected an ACK for our fin and DUT should enter CLOSE_WAIT: %s", err)
   116  			}
   117  			windowSize := seqnum.Size(*gotTCP.WindowSize)
   118  
   119  			sampleData := []byte("Sample Data")
   120  			samplePayload := &testbench.Payload{Bytes: sampleData}
   121  
   122  			// Send a segment with OTW Seq / unacc ACK.
   123  			conn.Send(t, tt.makeTestingTCP(t, &conn, tt.seqNumOffset, windowSize), samplePayload)
   124  			gotAck, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)}, time.Second)
   125  			if tt.expectAck && err != nil {
   126  				t.Errorf("expected an ack but got none: %s", err)
   127  			}
   128  			if err == nil && !tt.expectAck && gotAck != nil {
   129  				t.Errorf("expected no ack but got one: %s", gotAck)
   130  			}
   131  
   132  			// Now let's verify DUT is indeed in CLOSE_WAIT
   133  			dut.Close(t, acceptFD)
   134  			if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck | header.TCPFlagFin)}, time.Second); err != nil {
   135  				t.Fatalf("expected DUT to send a FIN: %s", err)
   136  			}
   137  			// Ack the FIN from DUT
   138  			conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)})
   139  			// Send some extra data to DUT
   140  			conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)}, samplePayload)
   141  			if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagRst)}, time.Second); err != nil {
   142  				t.Fatalf("expected DUT to send an RST: %s", err)
   143  			}
   144  		})
   145  	}
   146  }
   147  
   148  func TestActiveCloseUnaccpSeqAck(t *testing.T) {
   149  	for _, tt := range []struct {
   150  		description    string
   151  		makeTestingTCP func(t *testing.T, conn *testbench.TCPIPv4, seqNumOffset, windowSize seqnum.Size) testbench.TCP
   152  		seqNumOffset   seqnum.Size
   153  		restoreSeq     bool
   154  	}{
   155  		{description: "OTWSeq", makeTestingTCP: testbench.GenerateOTWSeqSegment, seqNumOffset: 0, restoreSeq: true},
   156  		{description: "OTWSeq", makeTestingTCP: testbench.GenerateOTWSeqSegment, seqNumOffset: 1, restoreSeq: true},
   157  		{description: "OTWSeq", makeTestingTCP: testbench.GenerateOTWSeqSegment, seqNumOffset: 2, restoreSeq: true},
   158  		{description: "UnaccAck", makeTestingTCP: testbench.GenerateUnaccACKSegment, seqNumOffset: 0, restoreSeq: false},
   159  		{description: "UnaccAck", makeTestingTCP: testbench.GenerateUnaccACKSegment, seqNumOffset: 1, restoreSeq: true},
   160  		{description: "UnaccAck", makeTestingTCP: testbench.GenerateUnaccACKSegment, seqNumOffset: 2, restoreSeq: true},
   161  	} {
   162  		t.Run(fmt.Sprintf("%s:offset=%d", tt.description, tt.seqNumOffset), func(t *testing.T) {
   163  			dut := testbench.NewDUT(t)
   164  			listenFD, remotePort := dut.CreateListener(t, unix.SOCK_STREAM, unix.IPPROTO_TCP, 1 /*backlog*/)
   165  			defer dut.Close(t, listenFD)
   166  			conn := dut.Net.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort})
   167  			defer conn.Close(t)
   168  
   169  			conn.Connect(t)
   170  			acceptFD, _ := dut.Accept(t, listenFD)
   171  
   172  			// Trigger active close.
   173  			dut.Shutdown(t, acceptFD, unix.SHUT_WR)
   174  
   175  			// Get to FIN_WAIT2
   176  			gotTCP, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagFin | header.TCPFlagAck)}, time.Second)
   177  			if err != nil {
   178  				t.Fatalf("expected a FIN: %s", err)
   179  			}
   180  			conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)})
   181  
   182  			sendUnaccSeqAck := func(state string) {
   183  				t.Helper()
   184  				sampleData := []byte("Sample Data")
   185  				samplePayload := &testbench.Payload{Bytes: sampleData}
   186  
   187  				origSeq := *conn.LocalSeqNum(t)
   188  				// Send a segment with OTW Seq / unacc ACK.
   189  				conn.Send(t, tt.makeTestingTCP(t, &conn, tt.seqNumOffset, seqnum.Size(*gotTCP.WindowSize)), samplePayload)
   190  				if tt.restoreSeq {
   191  					// Restore the local sequence number to ensure that the
   192  					// incoming ACK matches the TCP layer state.
   193  					*conn.LocalSeqNum(t) = origSeq
   194  				}
   195  				if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)}, time.Second); err != nil {
   196  					t.Errorf("expected an ack in %s state, but got none: %s", state, err)
   197  				}
   198  			}
   199  
   200  			sendUnaccSeqAck("FIN_WAIT2")
   201  
   202  			// Send a FIN to DUT to get to TIME_WAIT
   203  			conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagFin | header.TCPFlagAck)})
   204  			if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)}, time.Second); err != nil {
   205  				t.Fatalf("expected an ACK for our fin and DUT should enter TIME_WAIT: %s", err)
   206  			}
   207  
   208  			sendUnaccSeqAck("TIME_WAIT")
   209  		})
   210  	}
   211  }
   212  
   213  func TestSimultaneousCloseUnaccSeqAck(t *testing.T) {
   214  	for _, tt := range []struct {
   215  		description    string
   216  		makeTestingTCP func(t *testing.T, conn *testbench.TCPIPv4, seqNumOffset, windowSize seqnum.Size) testbench.TCP
   217  		seqNumOffset   seqnum.Size
   218  		expectAck      bool
   219  	}{
   220  		{description: "OTWSeq", makeTestingTCP: testbench.GenerateOTWSeqSegment, seqNumOffset: 0, expectAck: false},
   221  		{description: "OTWSeq", makeTestingTCP: testbench.GenerateOTWSeqSegment, seqNumOffset: 1, expectAck: true},
   222  		{description: "OTWSeq", makeTestingTCP: testbench.GenerateOTWSeqSegment, seqNumOffset: 2, expectAck: true},
   223  		{description: "UnaccAck", makeTestingTCP: testbench.GenerateUnaccACKSegment, seqNumOffset: 0, expectAck: false},
   224  		{description: "UnaccAck", makeTestingTCP: testbench.GenerateUnaccACKSegment, seqNumOffset: 1, expectAck: true},
   225  		{description: "UnaccAck", makeTestingTCP: testbench.GenerateUnaccACKSegment, seqNumOffset: 2, expectAck: true},
   226  	} {
   227  		t.Run(fmt.Sprintf("%s:offset=%d", tt.description, tt.seqNumOffset), func(t *testing.T) {
   228  			dut := testbench.NewDUT(t)
   229  			listenFD, remotePort := dut.CreateListener(t, unix.SOCK_STREAM, unix.IPPROTO_TCP, 1 /*backlog*/)
   230  			defer dut.Close(t, listenFD)
   231  			conn := dut.Net.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort})
   232  			defer conn.Close(t)
   233  
   234  			conn.Connect(t)
   235  			acceptFD, _ := dut.Accept(t, listenFD)
   236  
   237  			// Trigger active close.
   238  			dut.Shutdown(t, acceptFD, unix.SHUT_WR)
   239  
   240  			if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagFin | header.TCPFlagAck)}, time.Second); err != nil {
   241  				t.Fatalf("expected a FIN: %s", err)
   242  			}
   243  			// Do not ack the FIN from DUT so that we get to CLOSING.
   244  			seqNumForTheirFIN := testbench.Uint32(uint32(*conn.RemoteSeqNum(t)) - 1)
   245  			conn.Send(t, testbench.TCP{AckNum: seqNumForTheirFIN, Flags: testbench.TCPFlags(header.TCPFlagFin | header.TCPFlagAck)})
   246  
   247  			gotTCP, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)}, time.Second)
   248  			if err != nil {
   249  				t.Errorf("expected an ACK to our FIN, but got none: %s", err)
   250  			}
   251  
   252  			sampleData := []byte("Sample Data")
   253  			samplePayload := &testbench.Payload{Bytes: sampleData}
   254  
   255  			origSeq := uint32(*conn.LocalSeqNum(t))
   256  			// Send a segment with OTW Seq / unacc ACK.
   257  			tcp := tt.makeTestingTCP(t, &conn, tt.seqNumOffset, seqnum.Size(*gotTCP.WindowSize))
   258  			if tt.description == "OTWSeq" {
   259  				// If we generate an OTW Seq segment, make sure we don't acknowledge their FIN so that
   260  				// we stay in CLOSING.
   261  				tcp.AckNum = seqNumForTheirFIN
   262  			}
   263  			conn.Send(t, tcp, samplePayload)
   264  
   265  			got, err := conn.Expect(t, testbench.TCP{AckNum: testbench.Uint32(origSeq), Flags: testbench.TCPFlags(header.TCPFlagAck)}, time.Second)
   266  			if tt.expectAck && err != nil {
   267  				t.Errorf("expected an ack in CLOSING state, but got none: %s", err)
   268  			}
   269  			if !tt.expectAck && got != nil {
   270  				t.Errorf("expected no ack in CLOSING state, but got one: %s", got)
   271  			}
   272  		})
   273  	}
   274  }