gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/pkg/tcpip/transport/tcp/test/e2e/e2e.go (about) 1 // Copyright 2022 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 e2e contains definitions common to all e2e tcp tests. 16 package e2e 17 18 import ( 19 "bytes" 20 "testing" 21 "time" 22 23 "github.com/google/go-cmp/cmp" 24 "gvisor.dev/gvisor/pkg/tcpip" 25 "gvisor.dev/gvisor/pkg/tcpip/checker" 26 "gvisor.dev/gvisor/pkg/tcpip/header" 27 "gvisor.dev/gvisor/pkg/tcpip/seqnum" 28 "gvisor.dev/gvisor/pkg/tcpip/transport/tcp" 29 "gvisor.dev/gvisor/pkg/tcpip/transport/tcp/testing/context" 30 "gvisor.dev/gvisor/pkg/waiter" 31 ) 32 33 const ( 34 // DefaultMTU is the MTU, in bytes, used throughout the tests, except 35 // where another value is explicitly used. It is chosen to match the MTU 36 // of loopback interfaces on linux systems. 37 DefaultMTU = 65535 38 39 // DefaultIPv4MSS is the MSS sent by the network stack in SYN/SYN-ACK for an 40 // IPv4 endpoint when the MTU is set to defaultMTU in the test. 41 DefaultIPv4MSS = DefaultMTU - header.IPv4MinimumSize - header.TCPMinimumSize 42 43 // TSOptionSize is the size in bytes of the TCP timestamp option. 44 TSOptionSize = 12 45 46 // MaxTCPOptionSize is the maximum size TCP Options in a TCP header. 47 MaxTCPOptionSize = 40 48 ) 49 50 // CheckBrokenUpWrite does a large write > than the specified maxPayload and 51 // verifies that the received packets carry the expected payload and 52 // that the large write was broken up into > 1 packet. 53 func CheckBrokenUpWrite(t *testing.T, c *context.Context, maxPayload int) { 54 payloadMultiplier := 10 55 dataLen := payloadMultiplier * maxPayload 56 data := make([]byte, dataLen) 57 for i := range data { 58 data[i] = byte(i) 59 } 60 61 var r bytes.Reader 62 r.Reset(data) 63 if _, err := c.EP.Write(&r, tcpip.WriteOptions{}); err != nil { 64 t.Fatalf("Write failed: %s", err) 65 } 66 67 // Check that data is received in chunks. 68 bytesReceived := 0 69 numPackets := 0 70 iss := seqnum.Value(context.TestInitialSequenceNumber).Add(1) 71 for bytesReceived != dataLen { 72 v := c.GetPacket() 73 defer v.Release() 74 numPackets++ 75 tcpHdr := header.TCP(header.IPv4(v.AsSlice()).Payload()) 76 payloadLen := len(tcpHdr.Payload()) 77 checker.IPv4(t, v, 78 checker.TCP( 79 checker.DstPort(context.TestPort), 80 checker.TCPSeqNum(uint32(c.IRS)+1+uint32(bytesReceived)), 81 checker.TCPAckNum(uint32(iss)), 82 checker.TCPFlagsMatch(header.TCPFlagAck, ^header.TCPFlagPsh), 83 ), 84 ) 85 86 pdata := data[bytesReceived : bytesReceived+payloadLen] 87 if p := tcpHdr.Payload(); !bytes.Equal(pdata, p) { 88 t.Fatalf("got data = %v, want = %v", p, pdata) 89 } 90 bytesReceived += payloadLen 91 var options []byte 92 if c.TimeStampEnabled { 93 // If timestamp option is enabled, echo back the timestamp and increment 94 // the TSEcr value included in the packet and send that back as the TSVal. 95 parsedOpts := tcpHdr.ParsedOptions() 96 tsOpt := [12]byte{header.TCPOptionNOP, header.TCPOptionNOP} 97 header.EncodeTSOption(parsedOpts.TSEcr+1, parsedOpts.TSVal, tsOpt[2:]) 98 options = tsOpt[:] 99 } 100 // Acknowledge the data. 101 c.SendPacket(nil, &context.Headers{ 102 SrcPort: context.TestPort, 103 DstPort: c.Port, 104 Flags: header.TCPFlagAck, 105 SeqNum: iss, 106 AckNum: c.IRS.Add(1 + seqnum.Size(bytesReceived)), 107 RcvWnd: 30000, 108 TCPOpts: options, 109 }) 110 } 111 if numPackets == 1 { 112 t.Fatalf("expected write to be broken up into multiple packets, but got 1 packet") 113 } 114 } 115 116 // CreateConnectedWithSACKPermittedOption creates and connects c.ep with the 117 // SACKPermitted option enabled if the stack in the context has the SACK support 118 // enabled. 119 func CreateConnectedWithSACKPermittedOption(c *context.Context) *context.RawEndpoint { 120 return c.CreateConnectedWithOptionsNoDelay(header.TCPSynOptions{SACKPermitted: c.SACKEnabled()}) 121 } 122 123 // CreateConnectedWithSACKAndTS creates and connects c.ep with the SACK & TS 124 // option enabled if the stack in the context has SACK and TS enabled. 125 func CreateConnectedWithSACKAndTS(c *context.Context) *context.RawEndpoint { 126 return c.CreateConnectedWithOptionsNoDelay(header.TCPSynOptions{SACKPermitted: c.SACKEnabled(), TS: true}) 127 } 128 129 // SetStackSACKPermitted sets the tcpip.TCPSACKEnabled option of the context stack to 130 // enabled value. 131 func SetStackSACKPermitted(t *testing.T, c *context.Context, enable bool) { 132 t.Helper() 133 opt := tcpip.TCPSACKEnabled(enable) 134 if err := c.Stack().SetTransportProtocolOption(tcp.ProtocolNumber, &opt); err != nil { 135 t.Fatalf("c.s.SetTransportProtocolOption(%d, &%T(%t)): %s", tcp.ProtocolNumber, opt, opt, err) 136 } 137 } 138 139 // SetStackTCPRecovery sets the tcpip.TCPRecovery option of the context stack to 140 // the specified recovery value. 141 func SetStackTCPRecovery(t *testing.T, c *context.Context, recovery int) { 142 t.Helper() 143 opt := tcpip.TCPRecovery(recovery) 144 if err := c.Stack().SetTransportProtocolOption(header.TCPProtocolNumber, &opt); err != nil { 145 t.Fatalf("c.s.SetTransportProtocolOption(%d, &%v(%v)): %s", header.TCPProtocolNumber, opt, opt, err) 146 } 147 } 148 149 // SendAndReceiveWithSACK creates a SACK enabled connection w/ RACK enabled if 150 // enableRACK is true. It then proceeds to write a large payload and verifies 151 // that numPackets were received. 152 func SendAndReceiveWithSACK(t *testing.T, c *context.Context, maxPayload int, numPackets int, enableRACK bool) []byte { 153 SetStackSACKPermitted(t, c, true) 154 if !enableRACK { 155 SetStackTCPRecovery(t, c, 0) 156 } 157 // The delay should be below initial RTO (1s) otherwise retransimission 158 // will start. Choose a relatively large value so that estimated RTT 159 // keeps high even after a few rounds of undelayed RTT samples. 160 c.CreateConnectedWithOptions(header.TCPSynOptions{SACKPermitted: c.SACKEnabled(), TS: true}, 800*time.Millisecond /* delay */) 161 162 data := make([]byte, numPackets*maxPayload) 163 for i := range data { 164 data[i] = byte(i) 165 } 166 167 // Write the data. 168 var r bytes.Reader 169 r.Reset(data) 170 if _, err := c.EP.Write(&r, tcpip.WriteOptions{}); err != nil { 171 t.Fatalf("Write failed: %s", err) 172 } 173 174 bytesRead := 0 175 for i := 0; i < numPackets; i++ { 176 c.ReceiveAndCheckPacketWithOptions(data, bytesRead, maxPayload, TSOptionSize) 177 bytesRead += maxPayload 178 } 179 180 return data 181 } 182 183 // EnableCUBIC sets the CUBIC congestion control as the default congestion 184 // control algorithm for all newly created endpoints in the context stack. 185 func EnableCUBIC(t *testing.T, c *context.Context) { 186 t.Helper() 187 opt := tcpip.CongestionControlOption("cubic") 188 if err := c.Stack().SetTransportProtocolOption(tcp.ProtocolNumber, &opt); err != nil { 189 t.Fatalf("SetTransportProtocolOption(%d, &%T(%s)) %s", tcp.ProtocolNumber, opt, opt, err) 190 } 191 } 192 193 // TestV4Connect establishes an IPv4 Connection with the context stack. 194 func TestV4Connect(t *testing.T, c *context.Context, checkers ...checker.NetworkChecker) { 195 // Start connection attempt. 196 we, ch := waiter.NewChannelEntry(waiter.WritableEvents) 197 c.WQ.EventRegister(&we) 198 defer c.WQ.EventUnregister(&we) 199 200 err := c.EP.Connect(tcpip.FullAddress{Addr: context.TestV4MappedAddr, Port: context.TestPort}) 201 if d := cmp.Diff(&tcpip.ErrConnectStarted{}, err); d != "" { 202 t.Fatalf("c.EP.Connect(...) mismatch (-want +got):\n%s", d) 203 } 204 205 // Receive SYN packet. 206 v := c.GetPacket() 207 defer v.Release() 208 synCheckers := append(checkers, checker.TCP( 209 checker.DstPort(context.TestPort), 210 checker.TCPFlags(header.TCPFlagSyn), 211 )) 212 checker.IPv4(t, v, synCheckers...) 213 214 tcp := header.TCP(header.IPv4(v.AsSlice()).Payload()) 215 c.IRS = seqnum.Value(tcp.SequenceNumber()) 216 217 iss := seqnum.Value(789) 218 c.SendPacket(nil, &context.Headers{ 219 SrcPort: tcp.DestinationPort(), 220 DstPort: tcp.SourcePort(), 221 Flags: header.TCPFlagSyn | header.TCPFlagAck, 222 SeqNum: iss, 223 AckNum: c.IRS.Add(1), 224 RcvWnd: 30000, 225 }) 226 227 // Receive ACK packet. 228 ackCheckers := append(checkers, checker.TCP( 229 checker.DstPort(context.TestPort), 230 checker.TCPFlags(header.TCPFlagAck), 231 checker.TCPSeqNum(uint32(c.IRS)+1), 232 checker.TCPAckNum(uint32(iss)+1), 233 )) 234 235 v = c.GetPacket() 236 defer v.Release() 237 checker.IPv4(t, v, ackCheckers...) 238 239 // Wait for connection to be established. 240 select { 241 case <-ch: 242 if err := c.EP.LastError(); err != nil { 243 t.Fatalf("Unexpected error when connecting: %v", err) 244 } 245 case <-time.After(1 * time.Second): 246 t.Fatalf("Timed out waiting for connection") 247 } 248 } 249 250 // TestV6Connect establishes an IPv6 Connection with the context stack. 251 func TestV6Connect(t *testing.T, c *context.Context, checkers ...checker.NetworkChecker) { 252 // Start connection attempt to IPv6 address. 253 we, ch := waiter.NewChannelEntry(waiter.WritableEvents) 254 c.WQ.EventRegister(&we) 255 defer c.WQ.EventUnregister(&we) 256 257 err := c.EP.Connect(tcpip.FullAddress{Addr: context.TestV6Addr, Port: context.TestPort}) 258 if d := cmp.Diff(&tcpip.ErrConnectStarted{}, err); d != "" { 259 t.Fatalf("Connect(...) mismatch (-want +got):\n%s", d) 260 } 261 262 // Receive SYN packet. 263 v := c.GetV6Packet() 264 defer v.Release() 265 synCheckers := append(checkers, checker.TCP( 266 checker.DstPort(context.TestPort), 267 checker.TCPFlags(header.TCPFlagSyn), 268 )) 269 checker.IPv6(t, v, synCheckers...) 270 271 tcp := header.TCP(header.IPv6(v.AsSlice()).Payload()) 272 c.IRS = seqnum.Value(tcp.SequenceNumber()) 273 274 iss := seqnum.Value(789) 275 c.SendV6Packet(nil, &context.Headers{ 276 SrcPort: tcp.DestinationPort(), 277 DstPort: tcp.SourcePort(), 278 Flags: header.TCPFlagSyn | header.TCPFlagAck, 279 SeqNum: iss, 280 AckNum: c.IRS.Add(1), 281 RcvWnd: 30000, 282 }) 283 284 // Receive ACK packet. 285 ackCheckers := append(checkers, checker.TCP( 286 checker.DstPort(context.TestPort), 287 checker.TCPFlags(header.TCPFlagAck), 288 checker.TCPSeqNum(uint32(c.IRS)+1), 289 checker.TCPAckNum(uint32(iss)+1), 290 )) 291 v = c.GetV6Packet() 292 defer v.Release() 293 checker.IPv6(t, v, ackCheckers...) 294 295 // Wait for connection to be established. 296 select { 297 case <-ch: 298 if err := c.EP.LastError(); err != nil { 299 t.Fatalf("Unexpected error when connecting: %v", err) 300 } 301 case <-time.After(1 * time.Second): 302 t.Fatalf("Timed out waiting for connection") 303 } 304 }