From 18ae79231f0bd27e007a961dcb0a55cfc89860a8 Mon Sep 17 00:00:00 2001 From: "Bruce A. Mah" Date: Thu, 19 Feb 2026 14:51:17 -0800 Subject: [PATCH 1/3] Add GSO and GRO flags to JSON output. --- src/iperf_api.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/iperf_api.c b/src/iperf_api.c index 35dfc8932..8f344b62b 100644 --- a/src/iperf_api.c +++ b/src/iperf_api.c @@ -1,5 +1,5 @@ /* - * iperf, Copyright (c) 2014-2024, The Regents of the University of + * iperf, Copyright (c) 2014-2026, The Regents of the University of * California, through Lawrence Berkeley National Laboratory (subject * to receipt of any required approvals from the U.S. Dept. of * Energy). All rights reserved. @@ -920,7 +920,7 @@ void iperf_on_test_start(struct iperf_test *test) { if (test->json_output) { - cJSON_AddItemToObject(test->json_start, "test_start", iperf_json_printf("protocol: %s num_streams: %d blksize: %d omit: %d duration: %d bytes: %d blocks: %d reverse: %d tos: %d target_bitrate: %d bidir: %d fqrate: %d interval: %f", test->protocol->name, (int64_t) test->num_streams, (int64_t) test->settings->blksize, (int64_t) test->omit, (int64_t) test->duration, (int64_t) test->settings->bytes, (int64_t) test->settings->blocks, test->reverse?(int64_t)1:(int64_t)0, (int64_t) test->settings->tos, (int64_t) test->settings->rate, (int64_t) test->bidirectional, (uint64_t) test->settings->fqrate, test->stats_interval)); + cJSON_AddItemToObject(test->json_start, "test_start", iperf_json_printf("protocol: %s num_streams: %d blksize: %d omit: %d duration: %d bytes: %d blocks: %d reverse: %d tos: %d target_bitrate: %d bidir: %d fqrate: %d interval: %f gso: %d gro: %d", test->protocol->name, (int64_t) test->num_streams, (int64_t) test->settings->blksize, (int64_t) test->omit, (int64_t) test->duration, (int64_t) test->settings->bytes, (int64_t) test->settings->blocks, test->reverse?(int64_t)1:(int64_t)0, (int64_t) test->settings->tos, (int64_t) test->settings->rate, (int64_t) test->bidirectional, (uint64_t) test->settings->fqrate, test->stats_interval, (uint64_t) test->settings->gso, (uint64_t) test->settings->gro)); } else { if (test->verbose) { if (test->settings->bytes) From f8ece2fa5a183bdac4688e9d3c697551c365ad56 Mon Sep 17 00:00:00 2001 From: "Bruce A. Mah" Date: Thu, 19 Feb 2026 14:52:18 -0800 Subject: [PATCH 2/3] Add GSO/GRO feature availability to version output. --- src/iperf_util.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/iperf_util.c b/src/iperf_util.c index 2c005a541..9624a410f 100644 --- a/src/iperf_util.c +++ b/src/iperf_util.c @@ -1,5 +1,5 @@ /* - * iperf, Copyright (c) 2014, 2016, 2017, The Regents of the University of + * iperf, Copyright (c) 2014-2026, The Regents of the University of * California, through Lawrence Berkeley National Laboratory (subject * to receipt of any required approvals from the U.S. Dept. of * Energy). All rights reserved. @@ -347,6 +347,16 @@ get_optional_features(void) numfeatures++; #endif /* HAVE_PTHREAD */ +#if defined(HAVE_UDP_GRO) || defined(HAVE_UDP_SEGMENT) + if (numfeatures > 0) { + strncat(features, ", ", + sizeof(features) - strlen(features) - 1); + } + strncat(features, "GSO/GRO support", + sizeof(features) - strlen(features) - 1); + numfeatures++; +#endif /* HAVE_PTHREAD */ + if (numfeatures == 0) { strncat(features, "None", sizeof(features) - strlen(features) - 1); From 593b79d9790e6f0cd327cb452747935c94cd235b Mon Sep 17 00:00:00 2001 From: "Bruce A. Mah" Date: Thu, 19 Feb 2026 15:38:49 -0800 Subject: [PATCH 3/3] Minor adjustments in UDP code: Restore comments and debug logging in iperf_udp_recv(). Make some debug output in iperf_udp_send() conditional on DEBUG_LEVEL_DEBUG. Suggested by: @davidBar-On --- src/iperf_udp.c | 54 ++++++++++++++++++++++++++++++++++++++++++++---- src/iperf_util.c | 2 +- 2 files changed, 51 insertions(+), 5 deletions(-) diff --git a/src/iperf_udp.c b/src/iperf_udp.c index 371709a1e..393836c43 100644 --- a/src/iperf_udp.c +++ b/src/iperf_udp.c @@ -1,5 +1,5 @@ /* - * iperf, Copyright (c) 2014-2022, The Regents of the University of + * iperf, Copyright (c) 2014-2026, The Regents of the University of * California, through Lawrence Berkeley National Laboratory (subject * to receipt of any required approvals from the U.S. Dept. of * Energy). All rights reserved. @@ -156,22 +156,68 @@ iperf_udp_recv(struct iperf_stream *sp) sent_time.usecs = usec; } - /* Loss/out-of-order accounting - now always executed */ + /* + * Try to handle out of order packets. The way we do this + * uses a constant amount of storage but might not be + * correct in all cases. In particular we seem to have the + * assumption that packets can't be duplicated in the network, + * because duplicate packets will possibly cause some problems here. + * + * First figure out if the sequence numbers are going forward. + * Note that pcount is the sequence number read from the packet, + * and sp->packet_count is the highest sequence number seen so + * far (so we're expecting to see the packet with sequence number + * sp->packet_count + 1 arrive next). + */ if (pcount >= sp->packet_count + 1) { + + /* Forward, but is there a gap in sequence numbers? */ if (pcount > sp->packet_count + 1) { + /* There's a gap so count that as a loss. */ sp->cnt_error += (pcount - 1) - sp->packet_count; + if (test->debug_level >= DEBUG_LEVEL_INFO) + fprintf(stderr, "LOST %" PRIu64 " PACKETS - received packet %" PRIu64 " but expected sequence %" PRIu64 " on stream %d\n", (pcount - sp->packet_count + 1), pcount, sp->packet_count + 1, sp->socket); } + /* Update the highest sequence number seen so far. */ sp->packet_count = pcount; } else { + + /* + * Sequence number went backward (or was stationary?!?). + * This counts as an out-of-order packet. + */ sp->outoforder_packets++; + + /* + * If we have lost packets, then the fact that we are now + * seeing an out-of-order packet offsets a prior sequence + * number gap that was counted as a loss. So we can take + * away a loss. + */ if (sp->cnt_error > 0) sp->cnt_error--; + + /* Log the out-of-order packet */ + if (test->debug_level >= DEBUG_LEVEL_INFO) + fprintf(stderr, "OUT OF ORDER - received packet %" PRIu64 " but expected sequence %" PRIu64 " on stream %d\n", pcount, sp->packet_count + 1, sp->socket); } - /* Jitter computation - now always executed */ + /* + * jitter measurement + * + * This computation is based on RFC 1889 (specifically + * sections 6.3.1 and A.8). + * + * Note that synchronized clocks are not required since + * the source packet delta times are known. Also this + * computation does not require knowing the round-trip + * time. + */ iperf_time_now(&arrival_time); iperf_time_diff(&arrival_time, &sent_time, &temp_time); transit = iperf_time_in_secs(&temp_time); + + /* Hack to handle the first packet by initializing prev_transit. */ if (first_packet) sp->prev_transit = transit; d = transit - sp->prev_transit; @@ -234,7 +280,7 @@ iperf_udp_send(struct iperf_stream *sp) while (buf_sz > 0 && dgram_buf + dgram_sz <= dgram_buf_end) { cnt++; - if (sp->test->debug) + if (sp->test->debug_level >= DEBUG_LEVEL_DEBUG) printf("%d (%d) remaining %d\n", cnt, dgram_sz, buf_sz); /* Prevent buffer underflow */ diff --git a/src/iperf_util.c b/src/iperf_util.c index 9624a410f..1182bb933 100644 --- a/src/iperf_util.c +++ b/src/iperf_util.c @@ -355,7 +355,7 @@ get_optional_features(void) strncat(features, "GSO/GRO support", sizeof(features) - strlen(features) - 1); numfeatures++; -#endif /* HAVE_PTHREAD */ +#endif /* HAVE_UDP_GRO || HAVE_UDP_SEGMENT */ if (numfeatures == 0) { strncat(features, "None",