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