From 2110afb351789257ed2821f13a1fbd0ece2fd7c8 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 9 Jan 2026 16:36:52 +0000 Subject: [PATCH] [tcp] Report TCP statistics via the "ipstat" command Gather some basic statistics on TCP connections to allow out-of-order packets and duplicate packets to be observed even in non-debug builds. Report these statistics via the existing "ipstat" command, rather than introducing a separate "tcpstat" command, on the basis that we do not need the additional overhead of a separate command. Signed-off-by: Michael Brown --- src/include/ipxe/tcp.h | 17 +++++++++++++++++ src/net/tcp.c | 20 ++++++++++++++++++++ src/usr/ipstat.c | 14 ++++++++++++-- 3 files changed, 49 insertions(+), 2 deletions(-) diff --git a/src/include/ipxe/tcp.h b/src/include/ipxe/tcp.h index 1f65a3d92..a1e89f718 100644 --- a/src/include/ipxe/tcp.h +++ b/src/include/ipxe/tcp.h @@ -439,6 +439,23 @@ static inline int tcp_in_window ( uint32_t seq, uint32_t start, */ #define TCP_FINISH_TIMEOUT ( 1 * TICKS_PER_SEC ) +/** TCP statistics */ +struct tcp_statistics { + /** Number of packets received */ + unsigned long in_segs; + /** Total number of packets discarded due to lack of memory */ + unsigned long in_discards; + /** Total number of packets received out of order */ + unsigned long in_out_of_order; + + /** Number of octets received (including duplicate data) */ + unsigned long in_octets; + /** Number of octets processed and passed to upper layer */ + unsigned long in_octets_good; +}; + extern struct tcpip_protocol tcp_protocol __tcpip_protocol; +extern struct tcp_statistics tcp_stats; + #endif /* _IPXE_TCP_H */ diff --git a/src/net/tcp.c b/src/net/tcp.c index d47196ba5..2e52cf480 100644 --- a/src/net/tcp.c +++ b/src/net/tcp.c @@ -167,6 +167,9 @@ struct tcp_rx_queued_header { */ static LIST_HEAD ( tcp_conns ); +/** TCP statistics */ +struct tcp_statistics tcp_stats; + /** Transmit profiler */ static struct profiler tcp_tx_profiler __profiler = { .name = "tcp.tx" }; @@ -1216,6 +1219,9 @@ static int tcp_rx_data ( struct tcp_connection *tcp, uint32_t seq, /* Acknowledge new data */ tcp_rx_seq ( tcp, len ); + /* Update statistics */ + tcp_stats.in_octets_good += len; + /* Deliver data to application */ profile_start ( &tcp_xfer_profiler ); if ( ( rc = xfer_deliver_iob ( &tcp->xfer, iobuf ) ) != 0 ) { @@ -1299,6 +1305,7 @@ static void tcp_rx_enqueue ( struct tcp_connection *tcp, uint32_t seq, size_t len; uint32_t seq_len; uint32_t nxt; + uint32_t gap; /* Calculate remaining flags and sequence length. Note that * SYN, if present, has already been processed by this point. @@ -1330,12 +1337,18 @@ static void tcp_rx_enqueue ( struct tcp_connection *tcp, uint32_t seq, tcpqhdr->flags = flags; /* Add to RX queue */ + gap = tcp->rcv_ack; list_for_each_entry ( queued, &tcp->rx_queue, list ) { tcpqhdr = queued->data; if ( tcp_cmp ( seq, tcpqhdr->seq ) < 0 ) break; + gap = tcpqhdr->nxt; } list_add_tail ( &iobuf->list, &queued->list ); + + /* Update statistics */ + if ( seq != gap ) + tcp_stats.in_out_of_order++; } /** @@ -1459,6 +1472,10 @@ static int tcp_rx ( struct io_buffer *iobuf, seq_len = ( len + ( ( flags & TCP_SYN ) ? 1 : 0 ) + ( ( flags & TCP_FIN ) ? 1 : 0 ) ); + /* Update statistics */ + tcp_stats.in_segs++; + tcp_stats.in_octets += len; + /* Dump header */ DBGC2 ( tcp, "TCP %p RX %d<-%d %08x %08x..%08x %4zd", tcp, ntohs ( tcphdr->dest ), ntohs ( tcphdr->src ), @@ -1569,6 +1586,9 @@ static unsigned int tcp_discard ( void ) { list_del ( &iobuf->list ); free_iob ( iobuf ); + /* Update statistics */ + tcp_stats.in_discards++; + /* Report discard */ discarded++; break; diff --git a/src/usr/ipstat.c b/src/usr/ipstat.c index 0f09cc2ff..b9c5e02a7 100644 --- a/src/usr/ipstat.c +++ b/src/usr/ipstat.c @@ -24,23 +24,25 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include +#include #include #include /** @file * - * IP statistics + * TCP/IP statistics * */ /** - * Print IP statistics + * Print TCP/IP statistics * */ void ipstat ( void ) { struct ip_statistics_family *family; struct ip_statistics *stats; + /* Print per-family statistics */ for_each_table_entry ( family, IP_STATISTICS_FAMILIES ) { stats = family->stats; printf ( "IP version %d:\n", family->version ); @@ -63,4 +65,12 @@ void ipstat ( void ) { stats->out_mcast_pkts, stats->out_bcast_pkts, stats->out_octets ); } + + /* Print TCP statistics */ + printf ( "TCP:\n" ); + printf ( " InSegs:%ld InOctets:%ld InOctetsGood:%ld\n", + tcp_stats.in_segs, tcp_stats.in_octets, + tcp_stats.in_octets_good ); + printf ( " InDiscards:%ld InOutOfOrder:%ld\n", + tcp_stats.in_discards, tcp_stats.in_out_of_order ); }