Fix and merge tcp_{outside_the_window,tcp_unacc_seq_ack}_closing

The tests were not using the correct windowSize so the testing segments were
actually within the window for seqNumOffset=0 tests. The issue is already fixed
by #5674.

PiperOrigin-RevId: 364252630
This commit is contained in:
Zeling Feng
2021-03-22 00:04:17 -07:00
committed by gVisor bot
parent 7fac7e32f3
commit cbac2d9f97
6 changed files with 135 additions and 212 deletions
-10
View File
@@ -202,11 +202,6 @@ ALL_TESTS = [
PacketimpactTestInfo(
name = "tcp_outside_the_window",
),
PacketimpactTestInfo(
name = "tcp_outside_the_window_closing",
# TODO(b/181625316): Fix netstack then merge into tcp_outside_the_window.
expect_netstack_failure = True,
),
PacketimpactTestInfo(
name = "tcp_noaccept_close_rst",
),
@@ -216,11 +211,6 @@ ALL_TESTS = [
PacketimpactTestInfo(
name = "tcp_unacc_seq_ack",
),
PacketimpactTestInfo(
name = "tcp_unacc_seq_ack_closing",
# TODO(b/181625316): Fix netstack then merge into tcp_unacc_seq_ack.
expect_netstack_failure = True,
),
PacketimpactTestInfo(
name = "tcp_paws_mechanism",
# TODO(b/156682000): Fix netstack then remove the line below.
-22
View File
@@ -123,17 +123,6 @@ packetimpact_testbench(
],
)
packetimpact_testbench(
name = "tcp_outside_the_window_closing",
srcs = ["tcp_outside_the_window_closing_test.go"],
deps = [
"//pkg/tcpip/header",
"//pkg/tcpip/seqnum",
"//test/packetimpact/testbench",
"@org_golang_x_sys//unix:go_default_library",
],
)
packetimpact_testbench(
name = "tcp_noaccept_close_rst",
srcs = ["tcp_noaccept_close_rst_test.go"],
@@ -165,17 +154,6 @@ packetimpact_testbench(
],
)
packetimpact_testbench(
name = "tcp_unacc_seq_ack_closing",
srcs = ["tcp_unacc_seq_ack_closing_test.go"],
deps = [
"//pkg/tcpip/header",
"//pkg/tcpip/seqnum",
"//test/packetimpact/testbench",
"@org_golang_x_sys//unix:go_default_library",
],
)
packetimpact_testbench(
name = "tcp_paws_mechanism",
srcs = ["tcp_paws_mechanism_test.go"],
@@ -1,86 +0,0 @@
// Copyright 2021 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package tcp_outside_the_window_closing_test
import (
"flag"
"fmt"
"testing"
"time"
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/tcpip/header"
"gvisor.dev/gvisor/pkg/tcpip/seqnum"
"gvisor.dev/gvisor/test/packetimpact/testbench"
)
func init() {
testbench.Initialize(flag.CommandLine)
}
// TestAckOTWSeqInClosing tests that the DUT should send an ACK with
// the right ACK number when receiving a packet with OTW Seq number
// in CLOSING state. https://tools.ietf.org/html/rfc793#page-69
func TestAckOTWSeqInClosing(t *testing.T) {
for seqNumOffset := seqnum.Size(0); seqNumOffset < 3; seqNumOffset++ {
for _, tt := range []struct {
description string
flags header.TCPFlags
payloads testbench.Layers
}{
{"SYN", header.TCPFlagSyn, nil},
{"SYNACK", header.TCPFlagSyn | header.TCPFlagAck, nil},
{"ACK", header.TCPFlagAck, nil},
{"FINACK", header.TCPFlagFin | header.TCPFlagAck, nil},
{"Data", header.TCPFlagAck, []testbench.Layer{&testbench.Payload{Bytes: []byte("abc123")}}},
} {
t.Run(fmt.Sprintf("%s%d", tt.description, seqNumOffset), func(t *testing.T) {
dut := testbench.NewDUT(t)
listenFD, remotePort := dut.CreateListener(t, unix.SOCK_STREAM, unix.IPPROTO_TCP, 1)
defer dut.Close(t, listenFD)
conn := dut.Net.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort})
defer conn.Close(t)
conn.Connect(t)
acceptFD, _ := dut.Accept(t, listenFD)
defer dut.Close(t, acceptFD)
dut.Shutdown(t, acceptFD, unix.SHUT_WR)
if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagFin | header.TCPFlagAck)}, time.Second); err != nil {
t.Fatalf("expected FINACK from DUT, but got none: %s", err)
}
// Do not ack the FIN from DUT so that the TCP state on DUT is CLOSING instead of CLOSED.
seqNumForTheirFIN := testbench.Uint32(uint32(*conn.RemoteSeqNum(t)) - 1)
conn.Send(t, testbench.TCP{AckNum: seqNumForTheirFIN, Flags: testbench.TCPFlags(header.TCPFlagFin | header.TCPFlagAck)})
if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)}, time.Second); err != nil {
t.Errorf("expected an ACK to our FIN, but got none: %s", err)
}
windowSize := seqnum.Size(*conn.SynAck(t).WindowSize) + seqNumOffset
conn.SendFrameStateless(t, conn.CreateFrame(t, testbench.Layers{&testbench.TCP{
SeqNum: testbench.Uint32(uint32(conn.LocalSeqNum(t).Add(windowSize))),
AckNum: seqNumForTheirFIN,
Flags: testbench.TCPFlags(tt.flags),
}}, tt.payloads...))
if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)}, time.Second); err != nil {
t.Errorf("expected an ACK but got none: %s", err)
}
})
}
}
}
@@ -108,3 +108,75 @@ func TestTCPOutsideTheWindow(t *testing.T) {
})
}
}
// TestAckOTWSeqInClosing tests that the DUT should send an ACK with
// the right ACK number when receiving a packet with OTW Seq number
// in CLOSING state. https://tools.ietf.org/html/rfc793#page-69
func TestAckOTWSeqInClosing(t *testing.T) {
for _, tt := range []struct {
description string
flags header.TCPFlags
payloads testbench.Layers
seqNumOffset seqnum.Size
expectACK bool
}{
{"SYN", header.TCPFlagSyn, nil, 0, true},
{"SYNACK", header.TCPFlagSyn | header.TCPFlagAck, nil, 0, true},
{"ACK", header.TCPFlagAck, nil, 0, false},
{"FINACK", header.TCPFlagFin | header.TCPFlagAck, nil, 0, false},
{"Data", header.TCPFlagAck, []testbench.Layer{&testbench.Payload{Bytes: []byte("Sample Data")}}, 0, false},
{"SYN", header.TCPFlagSyn, nil, 1, true},
{"SYNACK", header.TCPFlagSyn | header.TCPFlagAck, nil, 1, true},
{"ACK", header.TCPFlagAck, nil, 1, true},
{"FINACK", header.TCPFlagFin | header.TCPFlagAck, nil, 1, true},
{"Data", header.TCPFlagAck, []testbench.Layer{&testbench.Payload{Bytes: []byte("Sample Data")}}, 1, true},
{"SYN", header.TCPFlagSyn, nil, 2, true},
{"SYNACK", header.TCPFlagSyn | header.TCPFlagAck, nil, 2, true},
{"ACK", header.TCPFlagAck, nil, 2, true},
{"FINACK", header.TCPFlagFin | header.TCPFlagAck, nil, 2, true},
{"Data", header.TCPFlagAck, []testbench.Layer{&testbench.Payload{Bytes: []byte("Sample Data")}}, 2, true},
} {
t.Run(fmt.Sprintf("%s%d", tt.description, tt.seqNumOffset), func(t *testing.T) {
dut := testbench.NewDUT(t)
listenFD, remotePort := dut.CreateListener(t, unix.SOCK_STREAM, unix.IPPROTO_TCP, 1)
defer dut.Close(t, listenFD)
conn := dut.Net.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort})
defer conn.Close(t)
conn.Connect(t)
acceptFD, _ := dut.Accept(t, listenFD)
defer dut.Close(t, acceptFD)
dut.Shutdown(t, acceptFD, unix.SHUT_WR)
if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagFin | header.TCPFlagAck)}, time.Second); err != nil {
t.Fatalf("expected FINACK from DUT, but got none: %s", err)
}
// Do not ack the FIN from DUT so that the TCP state on DUT is CLOSING instead of CLOSED.
seqNumForTheirFIN := testbench.Uint32(uint32(*conn.RemoteSeqNum(t)) - 1)
conn.Send(t, testbench.TCP{AckNum: seqNumForTheirFIN, Flags: testbench.TCPFlags(header.TCPFlagFin | header.TCPFlagAck)})
gotTCP, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)}, time.Second)
if err != nil {
t.Fatalf("expected an ACK to our FIN, but got none: %s", err)
}
windowSize := seqnum.Size(*gotTCP.WindowSize) + tt.seqNumOffset
conn.SendFrameStateless(t, conn.CreateFrame(t, testbench.Layers{&testbench.TCP{
SeqNum: testbench.Uint32(uint32(conn.LocalSeqNum(t).Add(windowSize))),
AckNum: seqNumForTheirFIN,
Flags: testbench.TCPFlags(tt.flags),
}}, tt.payloads...))
gotACK, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)}, time.Second)
if tt.expectACK && err != nil {
t.Errorf("expected an ACK but got none: %s", err)
}
if !tt.expectACK && gotACK != nil {
t.Errorf("expected no ACK but got one: %s", gotACK)
}
})
}
}
@@ -1,94 +0,0 @@
// Copyright 2021 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package tcp_unacc_seq_ack_closing_test
import (
"flag"
"fmt"
"testing"
"time"
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/tcpip/header"
"gvisor.dev/gvisor/pkg/tcpip/seqnum"
"gvisor.dev/gvisor/test/packetimpact/testbench"
)
func init() {
testbench.Initialize(flag.CommandLine)
}
func TestSimultaneousCloseUnaccSeqAck(t *testing.T) {
for _, tt := range []struct {
description string
makeTestingTCP func(t *testing.T, conn *testbench.TCPIPv4, seqNumOffset, windowSize seqnum.Size) testbench.TCP
seqNumOffset seqnum.Size
expectAck bool
}{
{description: "OTWSeq", makeTestingTCP: testbench.GenerateOTWSeqSegment, seqNumOffset: 0, expectAck: true},
{description: "OTWSeq", makeTestingTCP: testbench.GenerateOTWSeqSegment, seqNumOffset: 1, expectAck: true},
{description: "OTWSeq", makeTestingTCP: testbench.GenerateOTWSeqSegment, seqNumOffset: 2, expectAck: true},
{description: "UnaccAck", makeTestingTCP: testbench.GenerateUnaccACKSegment, seqNumOffset: 0, expectAck: false},
{description: "UnaccAck", makeTestingTCP: testbench.GenerateUnaccACKSegment, seqNumOffset: 1, expectAck: true},
{description: "UnaccAck", makeTestingTCP: testbench.GenerateUnaccACKSegment, seqNumOffset: 2, expectAck: true},
} {
t.Run(fmt.Sprintf("%s:offset=%d", tt.description, tt.seqNumOffset), func(t *testing.T) {
dut := testbench.NewDUT(t)
listenFD, remotePort := dut.CreateListener(t, unix.SOCK_STREAM, unix.IPPROTO_TCP, 1 /*backlog*/)
defer dut.Close(t, listenFD)
conn := dut.Net.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort})
defer conn.Close(t)
conn.Connect(t)
acceptFD, _ := dut.Accept(t, listenFD)
// Trigger active close.
dut.Shutdown(t, acceptFD, unix.SHUT_WR)
gotTCP, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagFin | header.TCPFlagAck)}, time.Second)
if err != nil {
t.Fatalf("expected a FIN: %s", err)
}
// Do not ack the FIN from DUT so that we get to CLOSING.
seqNumForTheirFIN := testbench.Uint32(uint32(*conn.RemoteSeqNum(t)) - 1)
conn.Send(t, testbench.TCP{AckNum: seqNumForTheirFIN, Flags: testbench.TCPFlags(header.TCPFlagFin | header.TCPFlagAck)})
if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)}, time.Second); err != nil {
t.Errorf("expected an ACK to our FIN, but got none: %s", err)
}
sampleData := []byte("Sample Data")
samplePayload := &testbench.Payload{Bytes: sampleData}
origSeq := uint32(*conn.LocalSeqNum(t))
// Send a segment with OTW Seq / unacc ACK.
tcp := tt.makeTestingTCP(t, &conn, tt.seqNumOffset, seqnum.Size(*gotTCP.WindowSize))
if tt.description == "OTWSeq" {
// If we generate an OTW Seq segment, make sure we don't acknowledge their FIN so that
// we stay in CLOSING.
tcp.AckNum = seqNumForTheirFIN
}
conn.Send(t, tcp, samplePayload)
got, err := conn.Expect(t, testbench.TCP{AckNum: testbench.Uint32(origSeq), Flags: testbench.TCPFlags(header.TCPFlagAck)}, time.Second)
if tt.expectAck && err != nil {
t.Errorf("expected an ack in CLOSING state, but got none: %s", err)
}
if !tt.expectAck && got != nil {
t.Errorf("expected no ack in CLOSING state, but got one: %s", got)
}
})
}
}
@@ -209,3 +209,66 @@ func TestActiveCloseUnaccpSeqAck(t *testing.T) {
})
}
}
func TestSimultaneousCloseUnaccSeqAck(t *testing.T) {
for _, tt := range []struct {
description string
makeTestingTCP func(t *testing.T, conn *testbench.TCPIPv4, seqNumOffset, windowSize seqnum.Size) testbench.TCP
seqNumOffset seqnum.Size
expectAck bool
}{
{description: "OTWSeq", makeTestingTCP: testbench.GenerateOTWSeqSegment, seqNumOffset: 0, expectAck: false},
{description: "OTWSeq", makeTestingTCP: testbench.GenerateOTWSeqSegment, seqNumOffset: 1, expectAck: true},
{description: "OTWSeq", makeTestingTCP: testbench.GenerateOTWSeqSegment, seqNumOffset: 2, expectAck: true},
{description: "UnaccAck", makeTestingTCP: testbench.GenerateUnaccACKSegment, seqNumOffset: 0, expectAck: false},
{description: "UnaccAck", makeTestingTCP: testbench.GenerateUnaccACKSegment, seqNumOffset: 1, expectAck: true},
{description: "UnaccAck", makeTestingTCP: testbench.GenerateUnaccACKSegment, seqNumOffset: 2, expectAck: true},
} {
t.Run(fmt.Sprintf("%s:offset=%d", tt.description, tt.seqNumOffset), func(t *testing.T) {
dut := testbench.NewDUT(t)
listenFD, remotePort := dut.CreateListener(t, unix.SOCK_STREAM, unix.IPPROTO_TCP, 1 /*backlog*/)
defer dut.Close(t, listenFD)
conn := dut.Net.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort})
defer conn.Close(t)
conn.Connect(t)
acceptFD, _ := dut.Accept(t, listenFD)
// Trigger active close.
dut.Shutdown(t, acceptFD, unix.SHUT_WR)
if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagFin | header.TCPFlagAck)}, time.Second); err != nil {
t.Fatalf("expected a FIN: %s", err)
}
// Do not ack the FIN from DUT so that we get to CLOSING.
seqNumForTheirFIN := testbench.Uint32(uint32(*conn.RemoteSeqNum(t)) - 1)
conn.Send(t, testbench.TCP{AckNum: seqNumForTheirFIN, Flags: testbench.TCPFlags(header.TCPFlagFin | header.TCPFlagAck)})
gotTCP, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)}, time.Second)
if err != nil {
t.Errorf("expected an ACK to our FIN, but got none: %s", err)
}
sampleData := []byte("Sample Data")
samplePayload := &testbench.Payload{Bytes: sampleData}
origSeq := uint32(*conn.LocalSeqNum(t))
// Send a segment with OTW Seq / unacc ACK.
tcp := tt.makeTestingTCP(t, &conn, tt.seqNumOffset, seqnum.Size(*gotTCP.WindowSize))
if tt.description == "OTWSeq" {
// If we generate an OTW Seq segment, make sure we don't acknowledge their FIN so that
// we stay in CLOSING.
tcp.AckNum = seqNumForTheirFIN
}
conn.Send(t, tcp, samplePayload)
got, err := conn.Expect(t, testbench.TCP{AckNum: testbench.Uint32(origSeq), Flags: testbench.TCPFlags(header.TCPFlagAck)}, time.Second)
if tt.expectAck && err != nil {
t.Errorf("expected an ack in CLOSING state, but got none: %s", err)
}
if !tt.expectAck && got != nil {
t.Errorf("expected no ack in CLOSING state, but got one: %s", got)
}
})
}
}