github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/tcpip/transport/tcp/tcp_timestamp_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 tcp_test 16 17 import ( 18 "bytes" 19 "math/rand" 20 "testing" 21 "time" 22 23 "github.com/google/go-cmp/cmp" 24 "github.com/SagerNet/gvisor/pkg/tcpip" 25 "github.com/SagerNet/gvisor/pkg/tcpip/checker" 26 "github.com/SagerNet/gvisor/pkg/tcpip/header" 27 "github.com/SagerNet/gvisor/pkg/tcpip/transport/tcp" 28 "github.com/SagerNet/gvisor/pkg/tcpip/transport/tcp/testing/context" 29 "github.com/SagerNet/gvisor/pkg/waiter" 30 ) 31 32 // createConnectedWithTimestampOption creates and connects c.ep with the 33 // timestamp option enabled. 34 func createConnectedWithTimestampOption(c *context.Context) *context.RawEndpoint { 35 return c.CreateConnectedWithOptions(header.TCPSynOptions{TS: true, TSVal: 1}) 36 } 37 38 // TestTimeStampEnabledConnect tests that netstack sends the timestamp option on 39 // an active connect and sets the TS Echo Reply fields correctly when the 40 // SYN-ACK also indicates support for the TS option and provides a TSVal. 41 func TestTimeStampEnabledConnect(t *testing.T) { 42 c := context.New(t, defaultMTU) 43 defer c.Cleanup() 44 45 rep := createConnectedWithTimestampOption(c) 46 47 // Register for read and validate that we have data to read. 48 we, ch := waiter.NewChannelEntry(nil) 49 c.WQ.EventRegister(&we, waiter.ReadableEvents) 50 defer c.WQ.EventUnregister(&we) 51 52 // The following tests ensure that TS option once enabled behaves 53 // correctly as described in 54 // https://tools.ietf.org/html/rfc7323#section-4.3. 55 // 56 // We are not testing delayed ACKs here, but we do test out of order 57 // packet delivery and filling the sequence number hole created due to 58 // the out of order packet. 59 // 60 // The test also verifies that the sequence numbers and timestamps are 61 // as expected. 62 data := []byte{1, 2, 3} 63 64 // First we increment tsVal by a small amount. 65 tsVal := rep.TSVal + 100 66 rep.SendPacketWithTS(data, tsVal) 67 rep.VerifyACKWithTS(tsVal) 68 69 // Next we send an out of order packet. 70 rep.NextSeqNum += 3 71 tsVal += 200 72 rep.SendPacketWithTS(data, tsVal) 73 74 // The ACK should contain the original sequenceNumber and an older TS. 75 rep.NextSeqNum -= 6 76 rep.VerifyACKWithTS(tsVal - 200) 77 78 // Next we fill the hole and the returned ACK should contain the 79 // cumulative sequence number acking all data sent till now and have the 80 // latest timestamp sent below in its TSEcr field. 81 tsVal -= 100 82 rep.SendPacketWithTS(data, tsVal) 83 rep.NextSeqNum += 3 84 rep.VerifyACKWithTS(tsVal) 85 86 // Increment tsVal by a large value that doesn't result in a wrap around. 87 tsVal += 0x7fffffff 88 rep.SendPacketWithTS(data, tsVal) 89 rep.VerifyACKWithTS(tsVal) 90 91 // Increment tsVal again by a large value which should cause the 92 // timestamp value to wrap around. The returned ACK should contain the 93 // wrapped around timestamp in its tsEcr field and not the tsVal from 94 // the previous packet sent above. 95 tsVal += 0x7fffffff 96 rep.SendPacketWithTS(data, tsVal) 97 rep.VerifyACKWithTS(tsVal) 98 99 select { 100 case <-ch: 101 case <-time.After(1 * time.Second): 102 t.Fatalf("Timed out waiting for data to arrive") 103 } 104 105 // There should be 5 views to read and each of them should 106 // contain the same data. 107 for i := 0; i < 5; i++ { 108 buf := make([]byte, len(data)) 109 w := tcpip.SliceWriter(buf) 110 result, err := c.EP.Read(&w, tcpip.ReadOptions{}) 111 if err != nil { 112 t.Fatalf("Unexpected error from Read: %v", err) 113 } 114 if diff := cmp.Diff(tcpip.ReadResult{ 115 Count: len(buf), 116 Total: len(buf), 117 }, result, checker.IgnoreCmpPath("ControlMessages")); diff != "" { 118 t.Errorf("Read: unexpected result (-want +got):\n%s", diff) 119 } 120 if got, want := buf, data; bytes.Compare(got, want) != 0 { 121 t.Fatalf("Data is different: got: %v, want: %v", got, want) 122 } 123 } 124 } 125 126 // TestTimeStampDisabledConnect tests that netstack sends timestamp option on an 127 // active connect but if the SYN-ACK doesn't specify the TS option then 128 // timestamp option is not enabled and future packets do not contain a 129 // timestamp. 130 func TestTimeStampDisabledConnect(t *testing.T) { 131 c := context.New(t, defaultMTU) 132 defer c.Cleanup() 133 134 c.CreateConnectedWithOptions(header.TCPSynOptions{}) 135 } 136 137 func timeStampEnabledAccept(t *testing.T, cookieEnabled bool, wndScale int, wndSize uint16) { 138 c := context.New(t, defaultMTU) 139 defer c.Cleanup() 140 141 if cookieEnabled { 142 opt := tcpip.TCPAlwaysUseSynCookies(true) 143 if err := c.Stack().SetTransportProtocolOption(tcp.ProtocolNumber, &opt); err != nil { 144 t.Fatalf("SetTransportProtocolOption(%d, &%T(%t)): %s", tcp.ProtocolNumber, opt, opt, err) 145 } 146 } 147 148 t.Logf("Test w/ CookieEnabled = %v", cookieEnabled) 149 tsVal := rand.Uint32() 150 c.AcceptWithOptions(wndScale, header.TCPSynOptions{MSS: defaultIPv4MSS, TS: true, TSVal: tsVal}) 151 152 // Now send some data and validate that timestamp is echoed correctly in the ACK. 153 data := []byte{1, 2, 3} 154 155 var r bytes.Reader 156 r.Reset(data) 157 if _, err := c.EP.Write(&r, tcpip.WriteOptions{}); err != nil { 158 t.Fatalf("Unexpected error from Write: %s", err) 159 } 160 161 // Check that data is received and that the timestamp option TSEcr field 162 // matches the expected value. 163 b := c.GetPacket() 164 checker.IPv4(t, b, 165 // Add 12 bytes for the timestamp option + 2 NOPs to align at 4 166 // byte boundary. 167 checker.PayloadLen(len(data)+header.TCPMinimumSize+12), 168 checker.TCP( 169 checker.DstPort(context.TestPort), 170 checker.TCPSeqNum(uint32(c.IRS)+1), 171 checker.TCPAckNum(790), 172 checker.TCPWindow(wndSize), 173 checker.TCPFlagsMatch(header.TCPFlagAck, ^header.TCPFlagPsh), 174 checker.TCPTimestampChecker(true, 0, tsVal+1), 175 ), 176 ) 177 } 178 179 // TestTimeStampEnabledAccept tests that if the SYN on a passive connect 180 // specifies the Timestamp option then the Timestamp option is sent on a SYN-ACK 181 // and echoes the tsVal field of the original SYN in the tcEcr field of the 182 // SYN-ACK. We cover the cases where SYN cookies are enabled/disabled and verify 183 // that Timestamp option is enabled in both cases if requested in the original 184 // SYN. 185 func TestTimeStampEnabledAccept(t *testing.T) { 186 testCases := []struct { 187 cookieEnabled bool 188 wndScale int 189 wndSize uint16 190 }{ 191 {true, -1, 0xffff}, // When cookie is used window scaling is disabled. 192 // DefaultReceiveBufferSize is 1MB >> 5. Advertised window will be 1/2 of that. 193 {false, 5, 0x4000}, 194 } 195 for _, tc := range testCases { 196 timeStampEnabledAccept(t, tc.cookieEnabled, tc.wndScale, tc.wndSize) 197 } 198 } 199 200 func timeStampDisabledAccept(t *testing.T, cookieEnabled bool, wndScale int, wndSize uint16) { 201 c := context.New(t, defaultMTU) 202 defer c.Cleanup() 203 204 if cookieEnabled { 205 opt := tcpip.TCPAlwaysUseSynCookies(true) 206 if err := c.Stack().SetTransportProtocolOption(tcp.ProtocolNumber, &opt); err != nil { 207 t.Fatalf("SetTransportProtocolOption(%d, &%T(%t)): %s", tcp.ProtocolNumber, opt, opt, err) 208 } 209 } 210 211 t.Logf("Test w/ CookieEnabled = %v", cookieEnabled) 212 c.AcceptWithOptions(wndScale, header.TCPSynOptions{MSS: defaultIPv4MSS}) 213 214 // Now send some data with the accepted connection endpoint and validate 215 // that no timestamp option is sent in the TCP segment. 216 data := []byte{1, 2, 3} 217 218 var r bytes.Reader 219 r.Reset(data) 220 if _, err := c.EP.Write(&r, tcpip.WriteOptions{}); err != nil { 221 t.Fatalf("Unexpected error from Write: %s", err) 222 } 223 224 // Check that data is received and that the timestamp option is disabled 225 // when SYN cookies are enabled/disabled. 226 b := c.GetPacket() 227 checker.IPv4(t, b, 228 checker.PayloadLen(len(data)+header.TCPMinimumSize), 229 checker.TCP( 230 checker.DstPort(context.TestPort), 231 checker.TCPSeqNum(uint32(c.IRS)+1), 232 checker.TCPAckNum(790), 233 checker.TCPWindow(wndSize), 234 checker.TCPFlagsMatch(header.TCPFlagAck, ^header.TCPFlagPsh), 235 checker.TCPTimestampChecker(false, 0, 0), 236 ), 237 ) 238 } 239 240 // TestTimeStampDisabledAccept tests that Timestamp option is not used when the 241 // peer doesn't advertise it and connection is established with Accept(). 242 func TestTimeStampDisabledAccept(t *testing.T) { 243 testCases := []struct { 244 cookieEnabled bool 245 wndScale int 246 wndSize uint16 247 }{ 248 {true, -1, 0xffff}, // When cookie is used window scaling is disabled. 249 // DefaultReceiveBufferSize is 1MB >> 5. Advertised window will be half of 250 // that. 251 {false, 5, 0x4000}, 252 } 253 for _, tc := range testCases { 254 timeStampDisabledAccept(t, tc.cookieEnabled, tc.wndScale, tc.wndSize) 255 } 256 } 257 258 func TestSendGreaterThanMTUWithOptions(t *testing.T) { 259 const maxPayload = 100 260 c := context.New(t, uint32(header.TCPMinimumSize+header.IPv4MinimumSize+maxPayload)) 261 defer c.Cleanup() 262 263 createConnectedWithTimestampOption(c) 264 testBrokenUpWrite(t, c, maxPayload) 265 } 266 267 func TestSegmentNotDroppedWhenTimestampMissing(t *testing.T) { 268 const maxPayload = 100 269 c := context.New(t, uint32(header.TCPMinimumSize+header.IPv4MinimumSize+maxPayload)) 270 defer c.Cleanup() 271 272 rep := createConnectedWithTimestampOption(c) 273 274 // Register for read. 275 we, ch := waiter.NewChannelEntry(nil) 276 c.WQ.EventRegister(&we, waiter.ReadableEvents) 277 defer c.WQ.EventUnregister(&we) 278 279 droppedPacketsStat := c.Stack().Stats().DroppedPackets 280 droppedPackets := droppedPacketsStat.Value() 281 data := []byte{1, 2, 3} 282 // Send a packet with no TCP options/timestamp. 283 rep.SendPacket(data, nil) 284 285 select { 286 case <-ch: 287 case <-time.After(1 * time.Second): 288 t.Fatalf("Timed out waiting for data to arrive") 289 } 290 291 // Assert that DroppedPackets was not incremented. 292 if got, want := droppedPacketsStat.Value(), droppedPackets; got != want { 293 t.Fatalf("incorrect number of dropped packets, got: %v, want: %v", got, want) 294 } 295 296 // Issue a read and we should data. 297 var buf bytes.Buffer 298 result, err := c.EP.Read(&buf, tcpip.ReadOptions{}) 299 if err != nil { 300 t.Fatalf("Unexpected error from Read: %v", err) 301 } 302 if diff := cmp.Diff(tcpip.ReadResult{ 303 Count: buf.Len(), 304 Total: buf.Len(), 305 }, result, checker.IgnoreCmpPath("ControlMessages")); diff != "" { 306 t.Errorf("Read: unexpected result (-want +got):\n%s", diff) 307 } 308 if got, want := buf.Bytes(), data; bytes.Compare(got, want) != 0 { 309 t.Fatalf("Data is different: got: %v, want: %v", got, want) 310 } 311 }