github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/test/packetimpact/tests/tcp_paws_mechanism_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_paws_mechanism_test
    16  
    17  import (
    18  	"encoding/hex"
    19  	"flag"
    20  	"testing"
    21  	"time"
    22  
    23  	"golang.org/x/sys/unix"
    24  	"github.com/SagerNet/gvisor/pkg/tcpip/header"
    25  	"github.com/SagerNet/gvisor/test/packetimpact/testbench"
    26  )
    27  
    28  func init() {
    29  	testbench.Initialize(flag.CommandLine)
    30  }
    31  
    32  func TestPAWSMechanism(t *testing.T) {
    33  	dut := testbench.NewDUT(t)
    34  	listenFD, remotePort := dut.CreateListener(t, unix.SOCK_STREAM, unix.IPPROTO_TCP, 1)
    35  	defer dut.Close(t, listenFD)
    36  	conn := dut.Net.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort})
    37  	defer conn.Close(t)
    38  
    39  	options := make([]byte, header.TCPOptionTSLength)
    40  	header.EncodeTSOption(currentTS(), 0, options)
    41  	conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagSyn), Options: options})
    42  	synAck, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagSyn | header.TCPFlagAck)}, time.Second)
    43  	if err != nil {
    44  		t.Fatalf("didn't get synack during handshake: %s", err)
    45  	}
    46  	parsedSynOpts := header.ParseSynOptions(synAck.Options, true)
    47  	if !parsedSynOpts.TS {
    48  		t.Fatalf("expected TSOpt from DUT, options we got:\n%s", hex.Dump(synAck.Options))
    49  	}
    50  	tsecr := parsedSynOpts.TSVal
    51  	header.EncodeTSOption(currentTS(), tsecr, options)
    52  	conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck), Options: options})
    53  	acceptFD, _ := dut.Accept(t, listenFD)
    54  	defer dut.Close(t, acceptFD)
    55  
    56  	sampleData := []byte("Sample Data")
    57  	sentTSVal := currentTS()
    58  	header.EncodeTSOption(sentTSVal, tsecr, options)
    59  	// 3ms here is chosen arbitrarily to make sure we have increasing timestamps
    60  	// every time we send one, it should not cause any flakiness because timestamps
    61  	// only need to be non-decreasing.
    62  	time.Sleep(3 * time.Millisecond)
    63  	conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck), Options: options}, &testbench.Payload{Bytes: sampleData})
    64  
    65  	gotTCP, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)}, time.Second)
    66  	if err != nil {
    67  		t.Fatalf("expected an ACK but got none: %s", err)
    68  	}
    69  
    70  	parsedOpts := header.ParseTCPOptions(gotTCP.Options)
    71  	if !parsedOpts.TS {
    72  		t.Fatalf("expected TS option in response, options we got:\n%s", hex.Dump(gotTCP.Options))
    73  	}
    74  	if parsedOpts.TSVal < tsecr {
    75  		t.Fatalf("TSVal should be non-decreasing, but %d < %d", parsedOpts.TSVal, tsecr)
    76  	}
    77  	if parsedOpts.TSEcr != sentTSVal {
    78  		t.Fatalf("TSEcr should match our sent TSVal, %d != %d", parsedOpts.TSEcr, sentTSVal)
    79  	}
    80  	tsecr = parsedOpts.TSVal
    81  	lastAckNum := gotTCP.AckNum
    82  
    83  	badTSVal := sentTSVal - 100
    84  	header.EncodeTSOption(badTSVal, tsecr, options)
    85  	// 3ms here is chosen arbitrarily and this time.Sleep() should not cause flakiness
    86  	// due to the exact same reasoning discussed above.
    87  	time.Sleep(3 * time.Millisecond)
    88  	conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck), Options: options}, &testbench.Payload{Bytes: sampleData})
    89  
    90  	gotTCP, err = conn.Expect(t, testbench.TCP{AckNum: lastAckNum, Flags: testbench.TCPFlags(header.TCPFlagAck)}, time.Second)
    91  	if err != nil {
    92  		t.Fatalf("expected segment with AckNum %d but got none: %s", lastAckNum, err)
    93  	}
    94  	parsedOpts = header.ParseTCPOptions(gotTCP.Options)
    95  	if !parsedOpts.TS {
    96  		t.Fatalf("expected TS option in response, options we got:\n%s", hex.Dump(gotTCP.Options))
    97  	}
    98  	if parsedOpts.TSVal < tsecr {
    99  		t.Fatalf("TSVal should be non-decreasing, but %d < %d", parsedOpts.TSVal, tsecr)
   100  	}
   101  	if parsedOpts.TSEcr != sentTSVal {
   102  		t.Fatalf("TSEcr should match our sent TSVal, %d != %d", parsedOpts.TSEcr, sentTSVal)
   103  	}
   104  }
   105  
   106  func currentTS() uint32 {
   107  	return uint32(time.Now().UnixNano() / 1e6)
   108  }