mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Test TCP behavior when receiving unacceptable segment in CLOSE_WAIT
TCP, in CLOSE-WAIT state, MUST return ACK with proper SEQ and ACK numbers after recv a seg with OTW SEQ or unacc ACK number, and remain in same state. If the connection is in a synchronized state, any unacceptable segment (out of window sequence number or unacceptable acknowledgment number) must elicit only an empty acknowledgment segment containing the current send-sequence number and an acknowledgment indicating the next sequence number expected to be received, and the connection remains in the same state. PiperOrigin-RevId: 306897984
This commit is contained in:
@@ -189,6 +189,7 @@ type tcpState struct {
|
||||
localSeqNum, remoteSeqNum *seqnum.Value
|
||||
synAck *TCP
|
||||
portPickerFD int
|
||||
finSent bool
|
||||
}
|
||||
|
||||
var _ layerState = (*tcpState)(nil)
|
||||
@@ -210,6 +211,7 @@ func newTCPState(out, in TCP) (*tcpState, error) {
|
||||
in: TCP{DstPort: &localPort},
|
||||
localSeqNum: SeqNumValue(seqnum.Value(rand.Uint32())),
|
||||
portPickerFD: portPickerFD,
|
||||
finSent: false,
|
||||
}
|
||||
if err := s.out.merge(&out); err != nil {
|
||||
return nil, err
|
||||
@@ -254,12 +256,18 @@ func (s *tcpState) sent(sent Layer) error {
|
||||
if !ok {
|
||||
return fmt.Errorf("can't update tcpState with %T Layer", sent)
|
||||
}
|
||||
for current := tcp.next(); current != nil; current = current.next() {
|
||||
s.localSeqNum.UpdateForward(seqnum.Size(current.length()))
|
||||
if !s.finSent {
|
||||
// update localSeqNum by the payload only when FIN is not yet sent by us
|
||||
for current := tcp.next(); current != nil; current = current.next() {
|
||||
s.localSeqNum.UpdateForward(seqnum.Size(current.length()))
|
||||
}
|
||||
}
|
||||
if tcp.Flags != nil && *tcp.Flags&(header.TCPFlagSyn|header.TCPFlagFin) != 0 {
|
||||
s.localSeqNum.UpdateForward(1)
|
||||
}
|
||||
if *tcp.Flags&(header.TCPFlagFin) != 0 {
|
||||
s.finSent = true
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -590,7 +598,7 @@ func (conn *TCPIPv4) RemoteSeqNum() *seqnum.Value {
|
||||
return conn.state().remoteSeqNum
|
||||
}
|
||||
|
||||
// LocalSeqNum returns the next expected sequence number from the DUT.
|
||||
// LocalSeqNum returns the next sequence number to send from the testbench.
|
||||
func (conn *TCPIPv4) LocalSeqNum() *seqnum.Value {
|
||||
return conn.state().localSeqNum
|
||||
}
|
||||
|
||||
@@ -75,6 +75,19 @@ packetimpact_go_test(
|
||||
],
|
||||
)
|
||||
|
||||
packetimpact_go_test(
|
||||
name = "tcp_close_wait_ack",
|
||||
srcs = ["tcp_close_wait_ack_test.go"],
|
||||
# TODO(b/153574037): Fix netstack then remove the line below.
|
||||
netstack = False,
|
||||
deps = [
|
||||
"//pkg/tcpip/header",
|
||||
"//pkg/tcpip/seqnum",
|
||||
"//test/packetimpact/testbench",
|
||||
"@org_golang_x_sys//unix:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
sh_binary(
|
||||
name = "test_runner",
|
||||
srcs = ["test_runner.sh"],
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
// Copyright 2020 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_close_wait_ack_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/seqnum"
|
||||
tb "gvisor.dev/gvisor/test/packetimpact/testbench"
|
||||
)
|
||||
|
||||
func TestCloseWaitAck(t *testing.T) {
|
||||
for _, tt := range []struct {
|
||||
description string
|
||||
makeTestingTCP func(conn *tb.TCPIPv4, seqNumOffset seqnum.Size) tb.TCP
|
||||
seqNumOffset seqnum.Size
|
||||
expectAck bool
|
||||
}{
|
||||
{"OTW", GenerateOTWSeqSegment, 0, false},
|
||||
{"OTW", GenerateOTWSeqSegment, 1, true},
|
||||
{"OTW", GenerateOTWSeqSegment, 2, true},
|
||||
{"ACK", GenerateUnaccACKSegment, 0, false},
|
||||
{"ACK", GenerateUnaccACKSegment, 1, true},
|
||||
{"ACK", GenerateUnaccACKSegment, 2, true},
|
||||
} {
|
||||
t.Run(fmt.Sprintf("%s%d", tt.description, tt.seqNumOffset), func(t *testing.T) {
|
||||
dut := tb.NewDUT(t)
|
||||
defer dut.TearDown()
|
||||
listenFd, remotePort := dut.CreateListener(unix.SOCK_STREAM, unix.IPPROTO_TCP, 1)
|
||||
defer dut.Close(listenFd)
|
||||
conn := tb.NewTCPIPv4(t, tb.TCP{DstPort: &remotePort}, tb.TCP{SrcPort: &remotePort})
|
||||
defer conn.Close()
|
||||
|
||||
conn.Handshake()
|
||||
acceptFd, _ := dut.Accept(listenFd)
|
||||
|
||||
// Send a FIN to DUT to intiate the active close
|
||||
conn.Send(tb.TCP{Flags: tb.Uint8(header.TCPFlagAck | header.TCPFlagFin)})
|
||||
if _, err := conn.Expect(tb.TCP{Flags: tb.Uint8(header.TCPFlagAck)}, time.Second); err != nil {
|
||||
t.Fatalf("expected an ACK for our fin and DUT should enter CLOSE_WAIT: %s", err)
|
||||
}
|
||||
|
||||
// Send a segment with OTW Seq / unacc ACK and expect an ACK back
|
||||
conn.Send(tt.makeTestingTCP(&conn, tt.seqNumOffset), &tb.Payload{Bytes: []byte("Sample Data")})
|
||||
gotAck, err := conn.Expect(tb.TCP{Flags: tb.Uint8(header.TCPFlagAck)}, time.Second)
|
||||
if tt.expectAck && err != nil {
|
||||
t.Fatalf("expected an ack but got none: %s", err)
|
||||
}
|
||||
if !tt.expectAck && gotAck != nil {
|
||||
t.Fatalf("expected no ack but got one: %s", gotAck)
|
||||
}
|
||||
|
||||
// Now let's verify DUT is indeed in CLOSE_WAIT
|
||||
dut.Close(acceptFd)
|
||||
if _, err := conn.Expect(tb.TCP{Flags: tb.Uint8(header.TCPFlagAck | header.TCPFlagFin)}, time.Second); err != nil {
|
||||
t.Fatalf("expected DUT to send a FIN: %s", err)
|
||||
}
|
||||
// Ack the FIN from DUT
|
||||
conn.Send(tb.TCP{Flags: tb.Uint8(header.TCPFlagAck)})
|
||||
// Send some extra data to DUT
|
||||
conn.Send(tb.TCP{Flags: tb.Uint8(header.TCPFlagAck)}, &tb.Payload{Bytes: []byte("Sample Data")})
|
||||
if _, err := conn.Expect(tb.TCP{Flags: tb.Uint8(header.TCPFlagRst)}, time.Second); err != nil {
|
||||
t.Fatalf("expected DUT to send an RST: %s", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// This generates an segment with seqnum = RCV.NXT + RCV.WND + seqNumOffset, the
|
||||
// generated segment is only acceptable when seqNumOffset is 0, otherwise an ACK
|
||||
// is expected from the receiver.
|
||||
func GenerateOTWSeqSegment(conn *tb.TCPIPv4, seqNumOffset seqnum.Size) tb.TCP {
|
||||
windowSize := seqnum.Size(*conn.SynAck().WindowSize)
|
||||
lastAcceptable := conn.LocalSeqNum().Add(windowSize - 1)
|
||||
otwSeq := uint32(lastAcceptable.Add(seqNumOffset))
|
||||
return tb.TCP{SeqNum: tb.Uint32(otwSeq), Flags: tb.Uint8(header.TCPFlagAck)}
|
||||
}
|
||||
|
||||
// This generates an segment with acknum = SND.NXT + seqNumOffset, the generated
|
||||
// segment is only acceptable when seqNumOffset is 0, otherwise an ACK is
|
||||
// expected from the receiver.
|
||||
func GenerateUnaccACKSegment(conn *tb.TCPIPv4, seqNumOffset seqnum.Size) tb.TCP {
|
||||
lastAcceptable := conn.RemoteSeqNum()
|
||||
unaccAck := uint32(lastAcceptable.Add(seqNumOffset))
|
||||
return tb.TCP{AckNum: tb.Uint32(unaccAck), Flags: tb.Uint8(header.TCPFlagAck)}
|
||||
}
|
||||
Reference in New Issue
Block a user