gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/pkg/tcpip/header/tcp_test.go (about)

     1  // Copyright 2018 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 header_test
    16  
    17  import (
    18  	"reflect"
    19  	"slices"
    20  	"testing"
    21  
    22  	"gvisor.dev/gvisor/pkg/tcpip/header"
    23  )
    24  
    25  func TestEncodeSACKBlocks(t *testing.T) {
    26  	testCases := []struct {
    27  		sackBlocks []header.SACKBlock
    28  		want       []header.SACKBlock
    29  		bufSize    int
    30  	}{
    31  		{
    32  			[]header.SACKBlock{{10, 20}, {22, 30}, {32, 40}, {42, 50}, {52, 60}, {62, 70}},
    33  			[]header.SACKBlock{{10, 20}, {22, 30}, {32, 40}, {42, 50}},
    34  			40,
    35  		},
    36  		{
    37  			[]header.SACKBlock{{10, 20}, {22, 30}, {32, 40}, {42, 50}, {52, 60}, {62, 70}},
    38  			[]header.SACKBlock{{10, 20}, {22, 30}, {32, 40}},
    39  			30,
    40  		},
    41  		{
    42  			[]header.SACKBlock{{10, 20}, {22, 30}, {32, 40}, {42, 50}, {52, 60}, {62, 70}},
    43  			[]header.SACKBlock{{10, 20}, {22, 30}},
    44  			20,
    45  		},
    46  		{
    47  			[]header.SACKBlock{{10, 20}, {22, 30}, {32, 40}, {42, 50}, {52, 60}, {62, 70}},
    48  			[]header.SACKBlock{{10, 20}},
    49  			10,
    50  		},
    51  		{
    52  			[]header.SACKBlock{{10, 20}, {22, 30}, {32, 40}, {42, 50}, {52, 60}, {62, 70}},
    53  			nil,
    54  			8,
    55  		},
    56  		{
    57  			[]header.SACKBlock{{10, 20}, {22, 30}, {32, 40}, {42, 50}, {52, 60}, {62, 70}},
    58  			[]header.SACKBlock{{10, 20}, {22, 30}, {32, 40}, {42, 50}},
    59  			60,
    60  		},
    61  	}
    62  	for _, tc := range testCases {
    63  		b := make([]byte, tc.bufSize)
    64  		t.Logf("testing: %v", tc)
    65  		header.EncodeSACKBlocks(tc.sackBlocks, b)
    66  		opts := header.ParseTCPOptions(b)
    67  		if got, want := opts.SACKBlocks, tc.want; !slices.Equal(got, want) {
    68  			t.Errorf("header.EncodeSACKBlocks(%v, %v), encoded blocks got: %v, want: %v", tc.sackBlocks, b, got, want)
    69  		}
    70  	}
    71  }
    72  
    73  func TestTCPParseOptions(t *testing.T) {
    74  	type tsOption struct {
    75  		tsVal uint32
    76  		tsEcr uint32
    77  	}
    78  
    79  	generateOptions := func(tsOpt *tsOption, sackBlocks []header.SACKBlock) []byte {
    80  		l := 0
    81  		if tsOpt != nil {
    82  			l += 10
    83  		}
    84  		if len(sackBlocks) != 0 {
    85  			l += len(sackBlocks)*8 + 2
    86  		}
    87  		b := make([]byte, l)
    88  		offset := 0
    89  		if tsOpt != nil {
    90  			offset = header.EncodeTSOption(tsOpt.tsVal, tsOpt.tsEcr, b)
    91  		}
    92  		header.EncodeSACKBlocks(sackBlocks, b[offset:])
    93  		return b
    94  	}
    95  
    96  	testCases := []struct {
    97  		b    []byte
    98  		want header.TCPOptions
    99  	}{
   100  		// Trivial cases.
   101  		{nil, header.TCPOptions{false, 0, 0, nil}},
   102  		{[]byte{header.TCPOptionNOP}, header.TCPOptions{false, 0, 0, nil}},
   103  		{[]byte{header.TCPOptionNOP, header.TCPOptionNOP}, header.TCPOptions{false, 0, 0, nil}},
   104  		{[]byte{header.TCPOptionEOL}, header.TCPOptions{false, 0, 0, nil}},
   105  		{[]byte{header.TCPOptionNOP, header.TCPOptionEOL, header.TCPOptionTS, 10, 1, 1}, header.TCPOptions{false, 0, 0, nil}},
   106  
   107  		// Test timestamp parsing.
   108  		{[]byte{header.TCPOptionNOP, header.TCPOptionTS, 10, 0, 0, 0, 1, 0, 0, 0, 1}, header.TCPOptions{true, 1, 1, nil}},
   109  		{[]byte{header.TCPOptionTS, 10, 0, 0, 0, 1, 0, 0, 0, 1}, header.TCPOptions{true, 1, 1, nil}},
   110  
   111  		// Test malformed timestamp option.
   112  		{[]byte{header.TCPOptionTS, 8, 1, 1}, header.TCPOptions{false, 0, 0, nil}},
   113  		{[]byte{header.TCPOptionNOP, header.TCPOptionTS, 8, 1, 1}, header.TCPOptions{false, 0, 0, nil}},
   114  		{[]byte{header.TCPOptionNOP, header.TCPOptionTS, 8, 0, 0, 0, 1, 0, 0, 0, 1}, header.TCPOptions{false, 0, 0, nil}},
   115  
   116  		// Test SACKBlock parsing.
   117  		{[]byte{header.TCPOptionSACK, 10, 0, 0, 0, 1, 0, 0, 0, 10}, header.TCPOptions{false, 0, 0, []header.SACKBlock{{1, 10}}}},
   118  		{[]byte{header.TCPOptionSACK, 18, 0, 0, 0, 1, 0, 0, 0, 10, 0, 0, 0, 11, 0, 0, 0, 12}, header.TCPOptions{false, 0, 0, []header.SACKBlock{{1, 10}, {11, 12}}}},
   119  
   120  		// Test malformed SACK option.
   121  		{[]byte{header.TCPOptionSACK, 0}, header.TCPOptions{false, 0, 0, nil}},
   122  		{[]byte{header.TCPOptionSACK, 8, 0, 0, 0, 1, 0, 0, 0, 10}, header.TCPOptions{false, 0, 0, nil}},
   123  		{[]byte{header.TCPOptionSACK, 11, 0, 0, 0, 1, 0, 0, 0, 10, 0, 0, 0, 11, 0, 0, 0, 12}, header.TCPOptions{false, 0, 0, nil}},
   124  		{[]byte{header.TCPOptionSACK, 17, 0, 0, 0, 1, 0, 0, 0, 10, 0, 0, 0, 11, 0, 0, 0, 12}, header.TCPOptions{false, 0, 0, nil}},
   125  		{[]byte{header.TCPOptionSACK}, header.TCPOptions{false, 0, 0, nil}},
   126  		{[]byte{header.TCPOptionSACK, 10}, header.TCPOptions{false, 0, 0, nil}},
   127  		{[]byte{header.TCPOptionSACK, 10, 0, 0, 0, 1, 0, 0, 0}, header.TCPOptions{false, 0, 0, nil}},
   128  
   129  		// Test Timestamp + SACK block parsing.
   130  		{generateOptions(&tsOption{1, 1}, []header.SACKBlock{{1, 10}, {11, 12}}), header.TCPOptions{true, 1, 1, []header.SACKBlock{{1, 10}, {11, 12}}}},
   131  		{generateOptions(&tsOption{1, 2}, []header.SACKBlock{{1, 10}, {11, 12}}), header.TCPOptions{true, 1, 2, []header.SACKBlock{{1, 10}, {11, 12}}}},
   132  		{generateOptions(&tsOption{1, 3}, []header.SACKBlock{{1, 10}, {11, 12}, {13, 14}, {14, 15}, {15, 16}}), header.TCPOptions{true, 1, 3, []header.SACKBlock{{1, 10}, {11, 12}, {13, 14}, {14, 15}}}},
   133  
   134  		// Test valid timestamp + malformed SACK block parsing.
   135  		{[]byte{header.TCPOptionTS, 10, 0, 0, 0, 1, 0, 0, 0, 1, header.TCPOptionSACK}, header.TCPOptions{true, 1, 1, nil}},
   136  		{[]byte{header.TCPOptionTS, 10, 0, 0, 0, 1, 0, 0, 0, 1, header.TCPOptionSACK, 10}, header.TCPOptions{true, 1, 1, nil}},
   137  		{[]byte{header.TCPOptionTS, 10, 0, 0, 0, 1, 0, 0, 0, 1, header.TCPOptionSACK, 10, 0, 0, 0}, header.TCPOptions{true, 1, 1, nil}},
   138  		{[]byte{header.TCPOptionTS, 10, 0, 0, 0, 1, 0, 0, 0, 1, header.TCPOptionSACK, 11, 0, 0, 0, 1, 0, 0, 0, 1}, header.TCPOptions{true, 1, 1, nil}},
   139  		{[]byte{header.TCPOptionSACK, header.TCPOptionTS, 10, 0, 0, 0, 1, 0, 0, 0, 1}, header.TCPOptions{false, 0, 0, nil}},
   140  		{[]byte{header.TCPOptionSACK, 10, header.TCPOptionTS, 10, 0, 0, 0, 1, 0, 0, 0, 1}, header.TCPOptions{false, 0, 0, []header.SACKBlock{{134873088, 65536}}}},
   141  		{[]byte{header.TCPOptionSACK, 10, 0, 0, 0, header.TCPOptionTS, 10, 0, 0, 0, 1, 0, 0, 0, 1}, header.TCPOptions{false, 0, 0, []header.SACKBlock{{8, 167772160}}}},
   142  		{[]byte{header.TCPOptionSACK, 11, 0, 0, 0, 1, 0, 0, 0, 1, header.TCPOptionTS, 10, 0, 0, 0, 1, 0, 0, 0, 1}, header.TCPOptions{false, 0, 0, nil}},
   143  	}
   144  	for _, tc := range testCases {
   145  		if got, want := header.ParseTCPOptions(tc.b), tc.want; !reflect.DeepEqual(got, want) {
   146  			t.Errorf("ParseTCPOptions(%v) = %v, want: %v", tc.b, got, tc.want)
   147  		}
   148  	}
   149  }
   150  
   151  func TestTCPFlags(t *testing.T) {
   152  	for _, tt := range []struct {
   153  		flags header.TCPFlags
   154  		want  string
   155  	}{
   156  		{header.TCPFlagFin, "F       "},
   157  		{header.TCPFlagSyn, " S      "},
   158  		{header.TCPFlagRst, "  R     "},
   159  		{header.TCPFlagPsh, "   P    "},
   160  		{header.TCPFlagAck, "    A   "},
   161  		{header.TCPFlagUrg, "     U  "},
   162  		{header.TCPFlagEce, "      E "},
   163  		{header.TCPFlagCwr, "       C"},
   164  		{header.TCPFlagSyn | header.TCPFlagAck, " S  A   "},
   165  		{header.TCPFlagFin | header.TCPFlagAck, "F   A   "},
   166  	} {
   167  		if got := tt.flags.String(); got != tt.want {
   168  			t.Errorf("got TCPFlags(%#b).String() = %s, want = %s", tt.flags, got, tt.want)
   169  		}
   170  	}
   171  }