Add a new TCP stat for current open connections.

Such a stat accounts for all connections that are currently
established and not yet transitioned to close state.
Also fix bug in double increment of CurrentEstablished stat.

Fixes #1579

PiperOrigin-RevId: 290827365
This commit is contained in:
Mithun Iyer
2020-01-21 15:43:39 -08:00
committed by gVisor bot
parent ad1968ed56
commit 7e6fbc6afe
6 changed files with 92 additions and 3 deletions
+2 -1
View File
@@ -150,7 +150,8 @@ var Metrics = tcpip.Stats{
TCP: tcpip.TCPStats{
ActiveConnectionOpenings: mustCreateMetric("/netstack/tcp/active_connection_openings", "Number of connections opened successfully via Connect."),
PassiveConnectionOpenings: mustCreateMetric("/netstack/tcp/passive_connection_openings", "Number of connections opened successfully via Listen."),
CurrentEstablished: mustCreateMetric("/netstack/tcp/current_established", "Number of connections in either ESTABLISHED or CLOSE-WAIT state now."),
CurrentEstablished: mustCreateMetric("/netstack/tcp/current_established", "Number of connections in ESTABLISHED state now."),
CurrentConnected: mustCreateMetric("/netstack/tcp/current_open", "Number of connections that are in connected state."),
EstablishedResets: mustCreateMetric("/netstack/tcp/established_resets", "Number of times TCP connections have made a direct transition to the CLOSED state from either the ESTABLISHED state or the CLOSE-WAIT state"),
EstablishedClosed: mustCreateMetric("/netstack/tcp/established_closed", "number of times established TCP connections made a transition to CLOSED state."),
EstablishedTimedout: mustCreateMetric("/netstack/tcp/established_timedout", "Number of times an established connection was reset because of keep-alive time out."),
+5 -1
View File
@@ -938,9 +938,13 @@ type TCPStats struct {
PassiveConnectionOpenings *StatCounter
// CurrentEstablished is the number of TCP connections for which the
// current state is either ESTABLISHED or CLOSE-WAIT.
// current state is ESTABLISHED.
CurrentEstablished *StatCounter
// CurrentConnected is the number of TCP connections that
// are in connected state.
CurrentConnected *StatCounter
// EstablishedResets is the number of times TCP connections have made
// a direct transition to the CLOSED state from either the
// ESTABLISHED state or the CLOSE-WAIT state.
-1
View File
@@ -562,7 +562,6 @@ func (e *endpoint) handleListenSegment(ctx *listenContext, s *segment) {
// Switch state to connected.
// We do not use transitionToStateEstablishedLocked here as there is
// no handshake state available when doing a SYN cookie based accept.
n.stack.Stats().TCP.CurrentEstablished.Increment()
n.isConnectNotified = true
n.setEndpointState(StateEstablished)
+1
View File
@@ -934,6 +934,7 @@ func (e *endpoint) transitionToStateCloseLocked() {
// 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()
}
+1
View File
@@ -594,6 +594,7 @@ func (e *endpoint) setEndpointState(state EndpointState) {
switch state {
case StateEstablished:
e.stack.Stats().TCP.CurrentEstablished.Increment()
e.stack.Stats().TCP.CurrentConnected.Increment()
case StateError:
fallthrough
case StateClose:
+83
View File
@@ -470,6 +470,89 @@ func TestConnectResetAfterClose(t *testing.T) {
}
}
// TestCurrentConnectedIncrement tests increment of the current
// established and connected counters.
func TestCurrentConnectedIncrement(t *testing.T) {
c := context.New(t, defaultMTU)
defer c.Cleanup()
// Set TCPTimeWaitTimeout to 1 seconds so that sockets are marked closed
// after 1 second in TIME_WAIT state.
tcpTimeWaitTimeout := 1 * time.Second
if err := c.Stack().SetTransportProtocolOption(tcp.ProtocolNumber, tcpip.TCPTimeWaitTimeoutOption(tcpTimeWaitTimeout)); err != nil {
t.Fatalf("c.stack.SetTransportProtocolOption(tcp, tcpip.TCPTimeWaitTimeout(%d) failed: %s", tcpTimeWaitTimeout, err)
}
c.CreateConnected(789, 30000, -1 /* epRcvBuf */)
ep := c.EP
c.EP = nil
if got := c.Stack().Stats().TCP.CurrentEstablished.Value(); got != 1 {
t.Errorf("got stats.TCP.CurrentEstablished.Value() = %v, want = 1", got)
}
gotConnected := c.Stack().Stats().TCP.CurrentConnected.Value()
if gotConnected != 1 {
t.Errorf("got stats.TCP.CurrentConnected.Value() = %v, want = 1", gotConnected)
}
ep.Close()
checker.IPv4(t, c.GetPacket(),
checker.TCP(
checker.DstPort(context.TestPort),
checker.SeqNum(uint32(c.IRS)+1),
checker.AckNum(790),
checker.TCPFlags(header.TCPFlagAck|header.TCPFlagFin),
),
)
c.SendPacket(nil, &context.Headers{
SrcPort: context.TestPort,
DstPort: c.Port,
Flags: header.TCPFlagAck,
SeqNum: 790,
AckNum: c.IRS.Add(2),
RcvWnd: 30000,
})
if got := c.Stack().Stats().TCP.CurrentEstablished.Value(); got != 0 {
t.Errorf("got stats.TCP.CurrentEstablished.Value() = %v, want = 0", got)
}
if got := c.Stack().Stats().TCP.CurrentConnected.Value(); got != gotConnected {
t.Errorf("got stats.TCP.CurrentConnected.Value() = %v, want = %v", got, gotConnected)
}
// Ack and send FIN as well.
c.SendPacket(nil, &context.Headers{
SrcPort: context.TestPort,
DstPort: c.Port,
Flags: header.TCPFlagAck | header.TCPFlagFin,
SeqNum: 790,
AckNum: c.IRS.Add(2),
RcvWnd: 30000,
})
// Check that the stack acks the FIN.
checker.IPv4(t, c.GetPacket(),
checker.PayloadLen(header.TCPMinimumSize),
checker.TCP(
checker.DstPort(context.TestPort),
checker.SeqNum(uint32(c.IRS)+2),
checker.AckNum(791),
checker.TCPFlags(header.TCPFlagAck),
),
)
// Wait for the TIME-WAIT state to transition to CLOSED.
time.Sleep(1 * time.Second)
if got := c.Stack().Stats().TCP.CurrentEstablished.Value(); got != 0 {
t.Errorf("got stats.TCP.CurrentEstablished.Value() = %v, want = 0", got)
}
if got := c.Stack().Stats().TCP.CurrentConnected.Value(); got != 0 {
t.Errorf("got stats.TCP.CurrentConnected.Value() = %v, want = 0", got)
}
}
// TestClosingWithEnqueuedSegments tests handling of still enqueued segments
// when the endpoint transitions to StateClose. The in-flight segments would be
// re-enqueued to a any listening endpoint.