github.com/polevpn/netstack@v1.10.9/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/polevpn/netstack/tcpip" 24 "github.com/polevpn/netstack/tcpip/buffer" 25 "github.com/polevpn/netstack/tcpip/checker" 26 "github.com/polevpn/netstack/tcpip/header" 27 "github.com/polevpn/netstack/tcpip/transport/tcp" 28 "github.com/polevpn/netstack/tcpip/transport/tcp/testing/context" 29 "github.com/polevpn/netstack/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.EventIn) 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 got, _, err := c.EP.Read(nil) 109 if err != nil { 110 t.Fatalf("Unexpected error from Read: %v", err) 111 } 112 if want := data; bytes.Compare(got, want) != 0 { 113 t.Fatalf("Data is different: got: %v, want: %v", got, want) 114 } 115 } 116 } 117 118 // TestTimeStampDisabledConnect tests that netstack sends timestamp option on an 119 // active connect but if the SYN-ACK doesn't specify the TS option then 120 // timestamp option is not enabled and future packets do not contain a 121 // timestamp. 122 func TestTimeStampDisabledConnect(t *testing.T) { 123 c := context.New(t, defaultMTU) 124 defer c.Cleanup() 125 126 c.CreateConnectedWithOptions(header.TCPSynOptions{}) 127 } 128 129 func timeStampEnabledAccept(t *testing.T, cookieEnabled bool, wndScale int, wndSize uint16) { 130 savedSynCountThreshold := tcp.SynRcvdCountThreshold 131 defer func() { 132 tcp.SynRcvdCountThreshold = savedSynCountThreshold 133 }() 134 135 if cookieEnabled { 136 tcp.SynRcvdCountThreshold = 0 137 } 138 c := context.New(t, defaultMTU) 139 defer c.Cleanup() 140 141 t.Logf("Test w/ CookieEnabled = %v", cookieEnabled) 142 tsVal := rand.Uint32() 143 c.AcceptWithOptions(wndScale, header.TCPSynOptions{MSS: defaultIPv4MSS, TS: true, TSVal: tsVal}) 144 145 // Now send some data and validate that timestamp is echoed correctly in the ACK. 146 data := []byte{1, 2, 3} 147 view := buffer.NewView(len(data)) 148 copy(view, data) 149 150 if _, _, err := c.EP.Write(tcpip.SlicePayload(view), tcpip.WriteOptions{}); err != nil { 151 t.Fatalf("Unexpected error from Write: %v", err) 152 } 153 154 // Check that data is received and that the timestamp option TSEcr field 155 // matches the expected value. 156 b := c.GetPacket() 157 checker.IPv4(t, b, 158 // Add 12 bytes for the timestamp option + 2 NOPs to align at 4 159 // byte boundary. 160 checker.PayloadLen(len(data)+header.TCPMinimumSize+12), 161 checker.TCP( 162 checker.DstPort(context.TestPort), 163 checker.SeqNum(uint32(c.IRS)+1), 164 checker.AckNum(790), 165 checker.Window(wndSize), 166 checker.TCPFlagsMatch(header.TCPFlagAck, ^uint8(header.TCPFlagPsh)), 167 checker.TCPTimestampChecker(true, 0, tsVal+1), 168 ), 169 ) 170 } 171 172 // TestTimeStampEnabledAccept tests that if the SYN on a passive connect 173 // specifies the Timestamp option then the Timestamp option is sent on a SYN-ACK 174 // and echoes the tsVal field of the original SYN in the tcEcr field of the 175 // SYN-ACK. We cover the cases where SYN cookies are enabled/disabled and verify 176 // that Timestamp option is enabled in both cases if requested in the original 177 // SYN. 178 func TestTimeStampEnabledAccept(t *testing.T) { 179 testCases := []struct { 180 cookieEnabled bool 181 wndScale int 182 wndSize uint16 183 }{ 184 {true, -1, 0xffff}, // When cookie is used window scaling is disabled. 185 {false, 5, 0x8000}, // DefaultReceiveBufferSize is 1MB >> 5. 186 } 187 for _, tc := range testCases { 188 timeStampEnabledAccept(t, tc.cookieEnabled, tc.wndScale, tc.wndSize) 189 } 190 } 191 192 func timeStampDisabledAccept(t *testing.T, cookieEnabled bool, wndScale int, wndSize uint16) { 193 savedSynCountThreshold := tcp.SynRcvdCountThreshold 194 defer func() { 195 tcp.SynRcvdCountThreshold = savedSynCountThreshold 196 }() 197 if cookieEnabled { 198 tcp.SynRcvdCountThreshold = 0 199 } 200 201 c := context.New(t, defaultMTU) 202 defer c.Cleanup() 203 204 t.Logf("Test w/ CookieEnabled = %v", cookieEnabled) 205 c.AcceptWithOptions(wndScale, header.TCPSynOptions{MSS: defaultIPv4MSS}) 206 207 // Now send some data with the accepted connection endpoint and validate 208 // that no timestamp option is sent in the TCP segment. 209 data := []byte{1, 2, 3} 210 view := buffer.NewView(len(data)) 211 copy(view, data) 212 213 if _, _, err := c.EP.Write(tcpip.SlicePayload(view), tcpip.WriteOptions{}); err != nil { 214 t.Fatalf("Unexpected error from Write: %v", err) 215 } 216 217 // Check that data is received and that the timestamp option is disabled 218 // when SYN cookies are enabled/disabled. 219 b := c.GetPacket() 220 checker.IPv4(t, b, 221 checker.PayloadLen(len(data)+header.TCPMinimumSize), 222 checker.TCP( 223 checker.DstPort(context.TestPort), 224 checker.SeqNum(uint32(c.IRS)+1), 225 checker.AckNum(790), 226 checker.Window(wndSize), 227 checker.TCPFlagsMatch(header.TCPFlagAck, ^uint8(header.TCPFlagPsh)), 228 checker.TCPTimestampChecker(false, 0, 0), 229 ), 230 ) 231 } 232 233 // TestTimeStampDisabledAccept tests that Timestamp option is not used when the 234 // peer doesn't advertise it and connection is established with Accept(). 235 func TestTimeStampDisabledAccept(t *testing.T) { 236 testCases := []struct { 237 cookieEnabled bool 238 wndScale int 239 wndSize uint16 240 }{ 241 {true, -1, 0xffff}, // When cookie is used window scaling is disabled. 242 {false, 5, 0x8000}, // DefaultReceiveBufferSize is 1MB >> 5. 243 } 244 for _, tc := range testCases { 245 timeStampDisabledAccept(t, tc.cookieEnabled, tc.wndScale, tc.wndSize) 246 } 247 } 248 249 func TestSendGreaterThanMTUWithOptions(t *testing.T) { 250 const maxPayload = 100 251 c := context.New(t, uint32(header.TCPMinimumSize+header.IPv4MinimumSize+maxPayload)) 252 defer c.Cleanup() 253 254 createConnectedWithTimestampOption(c) 255 testBrokenUpWrite(t, c, maxPayload) 256 } 257 258 func TestSegmentNotDroppedWhenTimestampMissing(t *testing.T) { 259 const maxPayload = 100 260 c := context.New(t, uint32(header.TCPMinimumSize+header.IPv4MinimumSize+maxPayload)) 261 defer c.Cleanup() 262 263 rep := createConnectedWithTimestampOption(c) 264 265 // Register for read. 266 we, ch := waiter.NewChannelEntry(nil) 267 c.WQ.EventRegister(&we, waiter.EventIn) 268 defer c.WQ.EventUnregister(&we) 269 270 droppedPacketsStat := c.Stack().Stats().DroppedPackets 271 droppedPackets := droppedPacketsStat.Value() 272 data := []byte{1, 2, 3} 273 // Send a packet with no TCP options/timestamp. 274 rep.SendPacket(data, nil) 275 276 select { 277 case <-ch: 278 case <-time.After(1 * time.Second): 279 t.Fatalf("Timed out waiting for data to arrive") 280 } 281 282 // Assert that DroppedPackets was not incremented. 283 if got, want := droppedPacketsStat.Value(), droppedPackets; got != want { 284 t.Fatalf("incorrect number of dropped packets, got: %v, want: %v", got, want) 285 } 286 287 // Issue a read and we should data. 288 got, _, err := c.EP.Read(nil) 289 if err != nil { 290 t.Fatalf("Unexpected error from Read: %v", err) 291 } 292 if want := data; bytes.Compare(got, want) != 0 { 293 t.Fatalf("Data is different: got: %v, want: %v", got, want) 294 } 295 }