Fix TCP CurrentConnected counter updates.

CurrentConnected counter is incorrectly decremented on close of an
endpoint which is still not connected.

Fixes #3443

PiperOrigin-RevId: 324155171
This commit is contained in:
Mithun Iyer
2020-07-30 22:49:30 -07:00
committed by gVisor bot
parent 98f9527c04
commit ad8164bb50
2 changed files with 102 additions and 56 deletions
+8 -3
View File
@@ -1024,14 +1024,19 @@ func (e *endpoint) transitionToStateEstablishedLocked(h *handshake) {
// delivered to this endpoint from the demuxer when the endpoint
// is transitioned to StateClose.
func (e *endpoint) transitionToStateCloseLocked() {
if e.EndpointState() == StateClose {
s := e.EndpointState()
if s == StateClose {
return
}
if s.connected() {
e.stack.Stats().TCP.CurrentConnected.Decrement()
e.stack.Stats().TCP.EstablishedClosed.Increment()
}
// Mark the endpoint as fully closed for reads/writes.
e.cleanupLocked()
e.setEndpointState(StateClose)
e.stack.Stats().TCP.CurrentConnected.Decrement()
e.stack.Stats().TCP.EstablishedClosed.Increment()
}
// tryDeliverSegmentFromClosedEndpoint attempts to deliver the parsed
+94 -53
View File
@@ -146,6 +146,24 @@ func TestActiveFailedConnectionAttemptIncrement(t *testing.T) {
}
}
func TestCloseWithoutConnect(t *testing.T) {
c := context.New(t, defaultMTU)
defer c.Cleanup()
// Create TCP endpoint.
var err *tcpip.Error
c.EP, err = c.Stack().NewEndpoint(tcp.ProtocolNumber, ipv4.ProtocolNumber, &c.WQ)
if err != nil {
t.Fatalf("NewEndpoint failed: %s", err)
}
c.EP.Close()
if got := c.Stack().Stats().TCP.CurrentConnected.Value(); got != 0 {
t.Errorf("got stats.TCP.CurrentConnected.Value() = %d, want = 0", got)
}
}
func TestTCPSegmentsSentIncrement(t *testing.T) {
c := context.New(t, defaultMTU)
defer c.Cleanup()
@@ -1276,68 +1294,91 @@ func TestConnectBindToDevice(t *testing.T) {
}
}
func TestRstOnSynSent(t *testing.T) {
c := context.New(t, defaultMTU)
defer c.Cleanup()
func TestSynSent(t *testing.T) {
for _, test := range []struct {
name string
reset bool
}{
{"RstOnSynSent", true},
{"CloseOnSynSent", false},
} {
t.Run(test.name, func(t *testing.T) {
c := context.New(t, defaultMTU)
defer c.Cleanup()
// Create an endpoint, don't handshake because we want to interfere with the
// handshake process.
c.Create(-1)
// Create an endpoint, don't handshake because we want to interfere with the
// handshake process.
c.Create(-1)
// Start connection attempt.
waitEntry, ch := waiter.NewChannelEntry(nil)
c.WQ.EventRegister(&waitEntry, waiter.EventOut)
defer c.WQ.EventUnregister(&waitEntry)
// Start connection attempt.
waitEntry, ch := waiter.NewChannelEntry(nil)
c.WQ.EventRegister(&waitEntry, waiter.EventOut)
defer c.WQ.EventUnregister(&waitEntry)
addr := tcpip.FullAddress{Addr: context.TestAddr, Port: context.TestPort}
if err := c.EP.Connect(addr); err != tcpip.ErrConnectStarted {
t.Fatalf("got Connect(%+v) = %s, want %s", addr, err, tcpip.ErrConnectStarted)
}
addr := tcpip.FullAddress{Addr: context.TestAddr, Port: context.TestPort}
if err := c.EP.Connect(addr); err != tcpip.ErrConnectStarted {
t.Fatalf("got Connect(%+v) = %s, want %s", addr, err, tcpip.ErrConnectStarted)
}
// Receive SYN packet.
b := c.GetPacket()
checker.IPv4(t, b,
checker.TCP(
checker.DstPort(context.TestPort),
checker.TCPFlags(header.TCPFlagSyn),
),
)
// Receive SYN packet.
b := c.GetPacket()
checker.IPv4(t, b,
checker.TCP(
checker.DstPort(context.TestPort),
checker.TCPFlags(header.TCPFlagSyn),
),
)
// Ensure that we've reached SynSent state
if got, want := tcp.EndpointState(c.EP.State()), tcp.StateSynSent; got != want {
t.Fatalf("got State() = %s, want %s", got, want)
}
tcpHdr := header.TCP(header.IPv4(b).Payload())
c.IRS = seqnum.Value(tcpHdr.SequenceNumber())
if got, want := tcp.EndpointState(c.EP.State()), tcp.StateSynSent; got != want {
t.Fatalf("got State() = %s, want %s", got, want)
}
tcpHdr := header.TCP(header.IPv4(b).Payload())
c.IRS = seqnum.Value(tcpHdr.SequenceNumber())
// Send a packet with a proper ACK and a RST flag to cause the socket
// to Error and close out
iss := seqnum.Value(789)
rcvWnd := seqnum.Size(30000)
c.SendPacket(nil, &context.Headers{
SrcPort: tcpHdr.DestinationPort(),
DstPort: tcpHdr.SourcePort(),
Flags: header.TCPFlagRst | header.TCPFlagAck,
SeqNum: iss,
AckNum: c.IRS.Add(1),
RcvWnd: rcvWnd,
TCPOpts: nil,
})
if test.reset {
// Send a packet with a proper ACK and a RST flag to cause the socket
// to error and close out.
iss := seqnum.Value(789)
rcvWnd := seqnum.Size(30000)
c.SendPacket(nil, &context.Headers{
SrcPort: tcpHdr.DestinationPort(),
DstPort: tcpHdr.SourcePort(),
Flags: header.TCPFlagRst | header.TCPFlagAck,
SeqNum: iss,
AckNum: c.IRS.Add(1),
RcvWnd: rcvWnd,
TCPOpts: nil,
})
} else {
c.EP.Close()
}
// Wait for receive to be notified.
select {
case <-ch:
case <-time.After(3 * time.Second):
t.Fatal("timed out waiting for packet to arrive")
}
// Wait for receive to be notified.
select {
case <-ch:
case <-time.After(3 * time.Second):
t.Fatal("timed out waiting for packet to arrive")
}
if _, _, err := c.EP.Read(nil); err != tcpip.ErrConnectionRefused {
t.Fatalf("got c.EP.Read(nil) = %s, want = %s", err, tcpip.ErrConnectionRefused)
}
if test.reset {
if _, _, err := c.EP.Read(nil); err != tcpip.ErrConnectionRefused {
t.Fatalf("got c.EP.Read(nil) = %s, want = %s", err, tcpip.ErrConnectionRefused)
}
} else {
if _, _, err := c.EP.Read(nil); err != tcpip.ErrAborted {
t.Fatalf("got c.EP.Read(nil) = %s, want = %s", err, tcpip.ErrAborted)
}
}
// Due to the RST the endpoint should be in an error state.
if got, want := tcp.EndpointState(c.EP.State()), tcp.StateError; got != want {
t.Fatalf("got State() = %s, want %s", got, want)
if got := c.Stack().Stats().TCP.CurrentConnected.Value(); got != 0 {
t.Errorf("got stats.TCP.CurrentConnected.Value() = %d, want = 0", got)
}
// Due to the RST the endpoint should be in an error state.
if got, want := tcp.EndpointState(c.EP.State()), tcp.StateError; got != want {
t.Fatalf("got State() = %s, want %s", got, want)
}
})
}
}