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 }