diff --git a/pkg/sentry/socket/netstack/netstack.go b/pkg/sentry/socket/netstack/netstack.go index 5beaace00..5f3e1819f 100644 --- a/pkg/sentry/socket/netstack/netstack.go +++ b/pkg/sentry/socket/netstack/netstack.go @@ -287,6 +287,7 @@ var Metrics = tcpip.Stats{ SegmentsAckedWithDSACK: mustCreateMetric("/netstack/tcp/segments_acked_with_dsack", "Number of segments for which DSACK was received."), SpuriousRecovery: mustCreateMetric("/netstack/tcp/spurious_recovery", "Number of times the connection entered loss recovery spuriously."), SpuriousRTORecovery: mustCreateMetric("/netstack/tcp/spurious_rto_recovery", "Number of times the connection entered RTO spuriously."), + ForwardMaxInFlightDrop: mustCreateMetric("/netstack/tcp/forward_max_in_flight_drop", "Number of connection requests dropped due to exceeding in-flight limit."), }, UDP: tcpip.UDPStats{ PacketsReceived: mustCreateMetric("/netstack/udp/packets_received", "Number of UDP datagrams received via HandlePacket."), diff --git a/pkg/tcpip/tcpip.go b/pkg/tcpip/tcpip.go index 513f6ab1f..19a34f617 100644 --- a/pkg/tcpip/tcpip.go +++ b/pkg/tcpip/tcpip.go @@ -2148,6 +2148,11 @@ type TCPStats struct { // SpuriousRTORecovery is the number of spurious RTOs. SpuriousRTORecovery *StatCounter + + // ForwardMaxInFlightDrop is the number of connection requests that are + // dropped due to exceeding the maximum number of in-flight connection + // requests. + ForwardMaxInFlightDrop *StatCounter } // UDPStats collects UDP-specific stats. diff --git a/pkg/tcpip/transport/tcp/forwarder.go b/pkg/tcpip/transport/tcp/forwarder.go index 3d632939d..0071093f2 100644 --- a/pkg/tcpip/transport/tcp/forwarder.go +++ b/pkg/tcpip/transport/tcp/forwarder.go @@ -88,6 +88,7 @@ func (f *Forwarder) HandlePacket(id stack.TransportEndpointID, pkt stack.PacketB // Ignore the segment if we're beyond the limit. if len(f.inFlight) >= f.maxInFlight { + f.stack.Stats().TCP.ForwardMaxInFlightDrop.Increment() return true } diff --git a/pkg/tcpip/transport/tcp/test/e2e/BUILD b/pkg/tcpip/transport/tcp/test/e2e/BUILD index 235a748bf..0c7610734 100644 --- a/pkg/tcpip/transport/tcp/test/e2e/BUILD +++ b/pkg/tcpip/transport/tcp/test/e2e/BUILD @@ -63,6 +63,7 @@ go_library( "//pkg/tcpip/checker", "//pkg/tcpip/header", "//pkg/tcpip/seqnum", + "//pkg/tcpip/stack", "//pkg/tcpip/transport/tcp", "//pkg/tcpip/transport/tcp/testing/context", "//pkg/waiter", @@ -81,6 +82,7 @@ go_test( "//pkg/tcpip/checker", "//pkg/tcpip/header", "//pkg/tcpip/seqnum", + "//pkg/tcpip/stack", "//pkg/tcpip/transport/tcp", "//pkg/tcpip/transport/tcp/testing/context", ], diff --git a/pkg/tcpip/transport/tcp/test/e2e/forwarder_test.go b/pkg/tcpip/transport/tcp/test/e2e/forwarder_test.go index 1edd12d5e..610e40909 100644 --- a/pkg/tcpip/transport/tcp/test/e2e/forwarder_test.go +++ b/pkg/tcpip/transport/tcp/test/e2e/forwarder_test.go @@ -169,6 +169,37 @@ func TestForwarderFailedConnect(t *testing.T) { } } +func TestForwarderDroppedStats(t *testing.T) { + const maxPayload = 100 + const mtu = 1200 + c := context.New(t, mtu) + defer c.Cleanup() + + s := c.Stack() + const maxInFlight = 2 + f := tcp.NewForwarder(s, 65536, maxInFlight, func(r *tcp.ForwarderRequest) { + // Complete all requests without doing anything + r.Complete(false) + }) + s.SetTransportProtocolHandler(tcp.ProtocolNumber, f.HandlePacket) + + for i := 0; i < maxInFlight*10; i++ { + iss := seqnum.Value(context.TestInitialSequenceNumber + i) + c.SendPacket(nil, &context.Headers{ + SrcPort: uint16(context.TestPort + i), + DstPort: context.StackPort, + Flags: header.TCPFlagSyn, + SeqNum: iss, + RcvWnd: 30000, + }) + } + + // Verify that we got some ignored packets + if curr := s.Stats().TCP.ForwardMaxInFlightDrop.Value(); curr == 0 { + t.Errorf("Expected at least one dropped connection") + } +} + func TestMain(m *testing.M) { refs.SetLeakMode(refs.LeaksPanic) code := m.Run()