gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/test/packetimpact/tests/tcp_outside_the_window_test.go (about) 1 // Copyright 2020 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_outside_the_window_test 16 17 import ( 18 "flag" 19 "fmt" 20 "testing" 21 "time" 22 23 "golang.org/x/sys/unix" 24 "gvisor.dev/gvisor/pkg/tcpip/header" 25 "gvisor.dev/gvisor/pkg/tcpip/seqnum" 26 "gvisor.dev/gvisor/test/packetimpact/testbench" 27 ) 28 29 func init() { 30 testbench.Initialize(flag.CommandLine) 31 } 32 33 // TestTCPOutsideTheWindows tests the behavior of the DUT when packets arrive 34 // that are inside or outside the TCP window. Packets that are outside the 35 // window should force an extra ACK, as described in RFC793 page 69: 36 // https://tools.ietf.org/html/rfc793#page-69 37 func TestTCPOutsideTheWindow(t *testing.T) { 38 for _, tt := range []struct { 39 description string 40 tcpFlags header.TCPFlags 41 payload []testbench.Layer 42 seqNumOffset seqnum.Size 43 expectACK bool 44 }{ 45 {"SYN", header.TCPFlagSyn, nil, 0, true}, 46 {"SYNACK", header.TCPFlagSyn | header.TCPFlagAck, nil, 0, true}, 47 {"ACK", header.TCPFlagAck, nil, 0, false}, 48 {"FIN", header.TCPFlagFin, nil, 0, false}, 49 {"Data", header.TCPFlagAck, []testbench.Layer{&testbench.Payload{Bytes: []byte("abc123")}}, 0, true}, 50 51 {"SYN", header.TCPFlagSyn, nil, 1, true}, 52 {"SYNACK", header.TCPFlagSyn | header.TCPFlagAck, nil, 1, true}, 53 {"ACK", header.TCPFlagAck, nil, 1, true}, 54 {"FIN", header.TCPFlagFin, nil, 1, false}, 55 {"Data", header.TCPFlagAck, []testbench.Layer{&testbench.Payload{Bytes: []byte("abc123")}}, 1, true}, 56 57 {"SYN", header.TCPFlagSyn, nil, 2, true}, 58 {"SYNACK", header.TCPFlagSyn | header.TCPFlagAck, nil, 2, true}, 59 {"ACK", header.TCPFlagAck, nil, 2, true}, 60 {"FIN", header.TCPFlagFin, nil, 2, false}, 61 {"Data", header.TCPFlagAck, []testbench.Layer{&testbench.Payload{Bytes: []byte("abc123")}}, 2, true}, 62 } { 63 t.Run(fmt.Sprintf("%s%d", tt.description, tt.seqNumOffset), func(t *testing.T) { 64 dut := testbench.NewDUT(t) 65 listenFD, remotePort := dut.CreateListener(t, unix.SOCK_STREAM, unix.IPPROTO_TCP, 1) 66 defer dut.Close(t, listenFD) 67 conn := dut.Net.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort}) 68 defer conn.Close(t) 69 conn.Connect(t) 70 acceptFD, _ := dut.Accept(t, listenFD) 71 defer dut.Close(t, acceptFD) 72 73 windowSize := seqnum.Size(*conn.SynAck(t).WindowSize) + tt.seqNumOffset 74 conn.Drain(t) 75 // Ignore whatever incrementing that this out-of-order packet might cause 76 // to the AckNum. 77 localSeqNum := testbench.Uint32(uint32(*conn.LocalSeqNum(t))) 78 conn.Send(t, testbench.TCP{ 79 Flags: testbench.TCPFlags(tt.tcpFlags), 80 SeqNum: testbench.Uint32(uint32(conn.LocalSeqNum(t).Add(windowSize))), 81 }, tt.payload...) 82 timeout := time.Second 83 gotACK, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck), AckNum: localSeqNum}, timeout) 84 if tt.expectACK && err != nil { 85 t.Fatalf("expected an ACK packet within %s but got none: %s", timeout, err) 86 } 87 // Data packets w/o SYN bits are always acked by Linux. Netstack ACK's data packets 88 // always right now. So only send a second segment and test for no ACK for packets 89 // with no data. 90 if tt.expectACK && tt.payload == nil { 91 // Sending another out-of-window segment immediately should not trigger 92 // an ACK if less than 500ms(default rate limit for out-of-window ACKs) 93 // has passed since the last ACK was sent. 94 t.Logf("sending another segment") 95 conn.Send(t, testbench.TCP{ 96 Flags: testbench.TCPFlags(tt.tcpFlags), 97 SeqNum: testbench.Uint32(uint32(conn.LocalSeqNum(t).Add(windowSize))), 98 }, tt.payload...) 99 timeout := 3 * time.Second 100 gotACK, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck), AckNum: localSeqNum}, timeout) 101 if err == nil { 102 t.Fatalf("expected no ACK packet but got one: %s", gotACK) 103 } 104 } 105 if !tt.expectACK && gotACK != nil { 106 t.Fatalf("expected no ACK packet within %s but got one: %s", timeout, gotACK) 107 } 108 }) 109 } 110 } 111 112 // TestAckOTWSeqInClosing tests that the DUT should send an ACK with 113 // the right ACK number when receiving a packet with OTW Seq number 114 // in CLOSING state. https://tools.ietf.org/html/rfc793#page-69 115 func TestAckOTWSeqInClosing(t *testing.T) { 116 for _, tt := range []struct { 117 description string 118 flags header.TCPFlags 119 payloads testbench.Layers 120 seqNumOffset seqnum.Size 121 expectACK bool 122 }{ 123 {"SYN", header.TCPFlagSyn, nil, 0, true}, 124 {"SYNACK", header.TCPFlagSyn | header.TCPFlagAck, nil, 0, true}, 125 {"ACK", header.TCPFlagAck, nil, 0, false}, 126 {"FINACK", header.TCPFlagFin | header.TCPFlagAck, nil, 0, false}, 127 {"Data", header.TCPFlagAck, []testbench.Layer{&testbench.Payload{Bytes: []byte("Sample Data")}}, 0, false}, 128 129 {"SYN", header.TCPFlagSyn, nil, 1, true}, 130 {"SYNACK", header.TCPFlagSyn | header.TCPFlagAck, nil, 1, true}, 131 {"ACK", header.TCPFlagAck, nil, 1, true}, 132 {"FINACK", header.TCPFlagFin | header.TCPFlagAck, nil, 1, true}, 133 {"Data", header.TCPFlagAck, []testbench.Layer{&testbench.Payload{Bytes: []byte("Sample Data")}}, 1, true}, 134 135 {"SYN", header.TCPFlagSyn, nil, 2, true}, 136 {"SYNACK", header.TCPFlagSyn | header.TCPFlagAck, nil, 2, true}, 137 {"ACK", header.TCPFlagAck, nil, 2, true}, 138 {"FINACK", header.TCPFlagFin | header.TCPFlagAck, nil, 2, true}, 139 {"Data", header.TCPFlagAck, []testbench.Layer{&testbench.Payload{Bytes: []byte("Sample Data")}}, 2, true}, 140 } { 141 t.Run(fmt.Sprintf("%s%d", tt.description, tt.seqNumOffset), func(t *testing.T) { 142 dut := testbench.NewDUT(t) 143 listenFD, remotePort := dut.CreateListener(t, unix.SOCK_STREAM, unix.IPPROTO_TCP, 1) 144 defer dut.Close(t, listenFD) 145 conn := dut.Net.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort}) 146 defer conn.Close(t) 147 conn.Connect(t) 148 acceptFD, _ := dut.Accept(t, listenFD) 149 defer dut.Close(t, acceptFD) 150 151 dut.Shutdown(t, acceptFD, unix.SHUT_WR) 152 153 if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagFin | header.TCPFlagAck)}, time.Second); err != nil { 154 t.Fatalf("expected FINACK from DUT, but got none: %s", err) 155 } 156 157 // Do not ack the FIN from DUT so that the TCP state on DUT is CLOSING instead of CLOSED. 158 seqNumForTheirFIN := testbench.Uint32(uint32(*conn.RemoteSeqNum(t)) - 1) 159 conn.Send(t, testbench.TCP{AckNum: seqNumForTheirFIN, Flags: testbench.TCPFlags(header.TCPFlagFin | header.TCPFlagAck)}) 160 161 gotTCP, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)}, time.Second) 162 if err != nil { 163 t.Fatalf("expected an ACK to our FIN, but got none: %s", err) 164 } 165 166 windowSize := seqnum.Size(*gotTCP.WindowSize) + tt.seqNumOffset 167 conn.SendFrameStateless(t, conn.CreateFrame(t, testbench.Layers{&testbench.TCP{ 168 SeqNum: testbench.Uint32(uint32(conn.LocalSeqNum(t).Add(windowSize))), 169 AckNum: seqNumForTheirFIN, 170 Flags: testbench.TCPFlags(tt.flags), 171 }}, tt.payloads...)) 172 173 gotACK, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)}, time.Second) 174 if tt.expectACK && err != nil { 175 t.Errorf("expected an ACK but got none: %s", err) 176 } 177 if !tt.expectACK && gotACK != nil { 178 t.Errorf("expected no ACK but got one: %s", gotACK) 179 } 180 }) 181 } 182 }