diff -Nru iperf-2.1.5+dfsg1/compat/gettcpinfo.c iperf-2.1.7+dfsg1/compat/gettcpinfo.c --- iperf-2.1.5+dfsg1/compat/gettcpinfo.c 2021-12-05 20:49:56.000000000 +0000 +++ iperf-2.1.7+dfsg1/compat/gettcpinfo.c 2022-04-11 18:13:03.000000000 +0000 @@ -52,51 +52,57 @@ #include "Thread.h" #endif -#if (HAVE_STRUCT_TCP_INFO_TCPI_TOTAL_RETRANS) && (HAVE_DECL_TCP_INFO) -inline void gettcpinfo (int sock, struct ReportStruct *sample) { - assert(sample); +#if HAVE_TCP_STATS +inline void gettcpinfo (int sock, struct iperf_tcpstats *stats) { + assert(stats); +#if HAVE_DECL_TCP_INFO struct tcp_info tcp_info_buf; socklen_t tcp_info_length = sizeof(struct tcp_info); - sample->tcpstats.isValid = false; if ((sock > 0) && \ !(getsockopt(sock, IPPROTO_TCP, TCP_INFO, &tcp_info_buf, &tcp_info_length) < 0)) { - sample->tcpstats.cwnd = tcp_info_buf.tcpi_snd_cwnd * tcp_info_buf.tcpi_snd_mss / 1024; - sample->tcpstats.rtt = tcp_info_buf.tcpi_rtt; - sample->tcpstats.rttvar = tcp_info_buf.tcpi_rttvar; - sample->tcpstats.retry_tot = tcp_info_buf.tcpi_total_retrans; - sample->tcpstats.isValid = true; - } else { - sample->tcpstats.cwnd = -1; - sample->tcpstats.rtt = 0; - sample->tcpstats.retry_tot = 0; - } -} + stats->cwnd = tcp_info_buf.tcpi_snd_cwnd * tcp_info_buf.tcpi_snd_mss / 1024; + stats->rtt = tcp_info_buf.tcpi_rtt; + stats->rttvar = tcp_info_buf.tcpi_rttvar; + stats->retry_tot = tcp_info_buf.tcpi_total_retrans; + stats->mss_negotiated = tcp_info_buf.tcpi_snd_mss; + stats->isValid = true; #elif HAVE_DECL_TCP_CONNECTION_INFO -inline void gettcpinfo (int sock, struct ReportStruct *sample) { - assert(sample); - struct tcp_connection_info tcp_info_buf; - socklen_t tcp_connection_info_length = sizeof(struct tcp_connection_info); - - sample->tcpstats.isValid = false; - if ((sock > 0) && \ - !(getsockopt(sock, IPPROTO_TCP, TCP_CONNECTION_INFO, &tcp_info_buf, &tcp_connection_info_length) < 0)) { - sample->tcpstats.cwnd = tcp_info_buf.tcpi_snd_cwnd / 1024; -// sample->tcpstats.rtt = tcp_info_buf.tcpi_rttcur * 1000; /current rtt units ms - sample->tcpstats.rtt = tcp_info_buf.tcpi_srtt * 1000; //average rtt units ms - sample->tcpstats.rtt = tcp_info_buf.tcpi_rttvar * 1000; - sample->tcpstats.retry_tot = tcp_info_buf.tcpi_txretransmitpackets; - sample->tcpstats.isValid = true; + struct tcp_connection_info tcp_info_buf; + socklen_t tcp_info_length = sizeof(struct tcp_connection_info); + if ((sock > 0) && \ + !(getsockopt(sock, IPPROTO_TCP, TCP_CONNECTION_INFO, &tcp_info_buf, &tcp_info_length) < 0)) { + stats->cwnd = tcp_info_buf.tcpi_snd_cwnd * tcp_info_buf.tcpi_maxseg / 1024; + stats->rtt = tcp_info_buf.tcpi_rttcur * 1000; // OS X units is ms + stats->rttvar = tcp_info_buf.tcpi_rttvar; + stats->retry_tot = tcp_info_buf.tcpi_txretransmitpackets; + stats->mss_negotiated = tcp_info_buf.tcpi_maxseg; + stats->isValid = true; +#endif } else { - sample->tcpstats.cwnd = -1; - sample->tcpstats.rtt = 0; - sample->tcpstats.retry_tot = 0; + stats->rtt = 1; + stats->isValid = false; } } -#elif WIN32 -inline void gettcpinfo (SOCKET sock, struct ReportStruct *sample) { - sample->tcpstats.rtt = 1; - sample->tcpstats.isValid = false; -}; +inline void tcpstats_copy (struct iperf_tcpstats *stats_dst, struct iperf_tcpstats *stats_src) { + stats_dst->cwnd = stats_src->cwnd; + stats_dst->rtt = stats_src->rtt; + stats_dst->rttvar = stats_src->rttvar; + stats_dst->mss_negotiated = stats_src->mss_negotiated; + stats_dst->retry_tot = stats_src->retry_tot; + stats_dst->connecttime = stats_src->connecttime; + stats_dst->isValid = stats_src->isValid; +} +#else +#if WIN32 +inline void gettcpinfo (SOCKET sock, struct iperf_tcpstats *stats) { #else -inline void gettcpinfo (int sock, struct ReportStruct *sample) { +inline void gettcpinfo (int sock, struct iperf_tcpstats *stats) { +#endif + stats->rtt = 1; + stats->isValid = false; +}; +inline void tcpstats_copy (struct iperf_tcpstats *stats_dst, struct iperf_tcpstats *stats_src) { + stats_dst->rtt = stats_src->rtt; + stats_dst->isValid = stats_src->isValid; +} #endif diff -Nru iperf-2.1.5+dfsg1/compat/signal.c iperf-2.1.7+dfsg1/compat/signal.c --- iperf-2.1.5+dfsg1/compat/signal.c 2021-12-05 20:49:56.000000000 +0000 +++ iperf-2.1.7+dfsg1/compat/signal.c 2022-04-11 18:13:03.000000000 +0000 @@ -183,6 +183,22 @@ #endif } +int set_itimer(int usecs) { + int err = 0; +#ifdef HAVE_SETITIMER + if (usecs < 0) { + WARN(1, "set_itimer value invalid"); + } else { + struct itimerval it; + memset (&it, 0, sizeof (it)); + it.it_value.tv_sec = (int)(usecs / 1000000); + it.it_value.tv_usec = (int)(usecs % 1000000); + err = setitimer(ITIMER_REAL, &it, NULL); + } +#endif + return err; +} + #ifdef __cplusplus } /* end extern "C" */ #endif diff -Nru iperf-2.1.5+dfsg1/config.h.in iperf-2.1.7+dfsg1/config.h.in --- iperf-2.1.5+dfsg1/config.h.in 2021-12-05 20:49:56.000000000 +0000 +++ iperf-2.1.7+dfsg1/config.h.in 2022-04-11 18:13:03.000000000 +0000 @@ -6,6 +6,10 @@ /* Define if debugging info is desired */ #undef DBG_MJZ +/* Define if default UDP payload length is based on device MTU and socket + buffer size */ +#undef DEFAULT_PAYLOAD_LEN_PER_MTU_DISCOVERY + /* AF_PACKET support is available */ #undef HAVE_AF_PACKET @@ -109,6 +113,10 @@ don't. */ #undef HAVE_DECL_SIGALRM +/* Define to 1 if you have the declaration of `SIOCGIFMTU', and to 0 if you + don't. */ +#undef HAVE_DECL_SIOCGIFMTU + /* Define to 1 if you have the declaration of `SO_BINDTODEVICE', and to 0 if you don't. */ #undef HAVE_DECL_SO_BINDTODEVICE @@ -141,6 +149,10 @@ don't. */ #undef HAVE_DECL_TCP_INFO +/* Define to 1 if you have the declaration of `TCP_MAXSEG', and to 0 if you + don't. */ +#undef HAVE_DECL_TCP_MAXSEG + /* Define to 1 if you have the declaration of `TCP_NODELAY', and to 0 if you don't. */ #undef HAVE_DECL_TCP_NODELAY @@ -149,6 +161,10 @@ you don't. */ #undef HAVE_DECL_TCP_NOTSENT_LOWAT +/* Define to 1 if you have the declaration of `TCP_QUICKACK', and to 0 if you + don't. */ +#undef HAVE_DECL_TCP_QUICKACK + /* Define to 1 if you have the declaration of `TCP_WINDOW_CLAMP', and to 0 if you don't. */ #undef HAVE_DECL_TCP_WINDOW_CLAMP @@ -217,6 +233,9 @@ /* Define to 1 if you have the header file. */ #undef HAVE_LINUX_IP_H +/* Define to 1 if you have the header file. */ +#undef HAVE_LINUX_SOCKIOS_H + /* Define to 1 if you have the header file. */ #undef HAVE_LINUX_UDP_H @@ -353,12 +372,18 @@ /* Define to 1 if you have the header file. */ #undef HAVE_SYSLOG_H +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_IOCTL_H + /* Define to 1 if you have the header file. */ #undef HAVE_SYS_SELECT_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_SOCKET_H +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_SOCKIO_H + /* Define to 1 if you have the header file. */ #undef HAVE_SYS_STAT_H diff -Nru iperf-2.1.5+dfsg1/configure iperf-2.1.7+dfsg1/configure --- iperf-2.1.5+dfsg1/configure 2021-12-05 20:56:21.000000000 +0000 +++ iperf-2.1.7+dfsg1/configure 2022-04-11 18:15:31.000000000 +0000 @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for Iperf 2.1.5. +# Generated by GNU Autoconf 2.69 for Iperf 2.1.7. # # # Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. @@ -577,8 +577,8 @@ # Identity of this package. PACKAGE_NAME='Iperf' PACKAGE_TARNAME='iperf' -PACKAGE_VERSION='2.1.5' -PACKAGE_STRING='Iperf 2.1.5' +PACKAGE_VERSION='2.1.7' +PACKAGE_STRING='Iperf 2.1.7' PACKAGE_BUGREPORT='' PACKAGE_URL='' @@ -683,6 +683,8 @@ PACKET_DEBUG_TRUE THREAD_DEBUG_FALSE THREAD_DEBUG_TRUE +DISCOVER_DEFAULTLEN_FALSE +DISCOVER_DEFAULTLEN_TRUE FASTSAMPLING_FALSE FASTSAMPLING_TRUE MAINT @@ -770,6 +772,7 @@ enable_default_localonly enable_seqno64b enable_fastsampling +enable_discover_defaultlen enable_thread_debug enable_packet_debug enable_checkprograms @@ -1332,7 +1335,7 @@ # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures Iperf 2.1.5 to adapt to many kinds of systems. +\`configure' configures Iperf 2.1.7 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1402,7 +1405,7 @@ if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of Iperf 2.1.5:";; + short | recursive ) echo "Configuration of Iperf 2.1.7:";; esac cat <<\_ACEOF @@ -1433,6 +1436,9 @@ enable) --enable-fastsampling enable support for 100 microsecond report intervals (default is disable) + --enable-discover-defaultlen + enable support to set the default payload size after + device MTU discovery (default is disable) --enable-thread-debug enable support for thread debugging (default is disable) --enable-packet-debug enable support for packet level debugging (default @@ -1527,7 +1533,7 @@ test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -Iperf configure 2.1.5 +Iperf configure 2.1.7 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -2087,7 +2093,7 @@ This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by Iperf $as_me 2.1.5, which was +It was created by Iperf $as_me 2.1.7, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -2952,7 +2958,7 @@ # Define the identity of the package. PACKAGE='iperf' - VERSION='2.1.5' + VERSION='2.1.7' cat >>confdefs.h <<_ACEOF @@ -3279,6 +3285,20 @@ fi +# Check whether --enable-discover_defaultlen was given. +if test "${enable_discover_defaultlen+set}" = set; then : + enableval=$enable_discover_defaultlen; +fi + + if test "x$enable_discover_defaultlen" = "xyes"; then + DISCOVER_DEFAULTLEN_TRUE= + DISCOVER_DEFAULTLEN_FALSE='#' +else + DISCOVER_DEFAULTLEN_TRUE='#' + DISCOVER_DEFAULTLEN_FALSE= +fi + + # Check whether --enable-thread_debug was given. if test "${enable_thread_debug+set}" = set; then : enableval=$enable_thread_debug; @@ -7188,7 +7208,7 @@ done -for ac_header in arpa/inet.h libintl.h net/ethernet.h net/if.h linux/ip.h linux/udp.h linux/if_packet.h linux/filter.h linux/if_tun.h netdb.h netinet/in.h netinet/tcp.h stdlib.h string.h strings.h sys/socket.h sys/time.h syslog.h unistd.h signal.h ifaddrs.h +for ac_header in arpa/inet.h libintl.h net/ethernet.h net/if.h sys/ioctl.h sys/sockio.h linux/sockios.h linux/ip.h linux/udp.h linux/if_packet.h linux/filter.h linux/if_tun.h netdb.h netinet/in.h netinet/tcp.h stdlib.h string.h strings.h sys/socket.h sys/time.h syslog.h unistd.h signal.h ifaddrs.h do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" @@ -7930,6 +7950,25 @@ #define HAVE_DECL_EWOULDBLOCK $ac_have_decl _ACEOF +ac_fn_c_check_decl "$LINENO" "SIOCGIFMTU" "ac_cv_have_decl_SIOCGIFMTU" " +#ifdef HAVE_LINUX_SOCKIOS_H +#include +#endif +#ifdef HAVE_SYS_SOCKIO_H +#include +#endif + +" +if test "x$ac_cv_have_decl_SIOCGIFMTU" = xyes; then : + ac_have_decl=1 +else + ac_have_decl=0 +fi + +cat >>confdefs.h <<_ACEOF +#define HAVE_DECL_SIOCGIFMTU $ac_have_decl +_ACEOF + ac_fn_c_check_decl "$LINENO" "pthread_cancel" "ac_cv_have_decl_pthread_cancel" "#include " if test "x$ac_cv_have_decl_pthread_cancel" = xyes; then : @@ -8238,6 +8277,17 @@ cat >>confdefs.h <<_ACEOF #define HAVE_DECL_TCP_WINDOW_CLAMP $ac_have_decl _ACEOF +ac_fn_c_check_decl "$LINENO" "TCP_QUICKACK" "ac_cv_have_decl_TCP_QUICKACK" "$in_h +" +if test "x$ac_cv_have_decl_TCP_QUICKACK" = xyes; then : + ac_have_decl=1 +else + ac_have_decl=0 +fi + +cat >>confdefs.h <<_ACEOF +#define HAVE_DECL_TCP_QUICKACK $ac_have_decl +_ACEOF ac_fn_c_check_decl "$LINENO" "TCP_NOTSENT_LOWAT" "ac_cv_have_decl_TCP_NOTSENT_LOWAT" "$in_h " if test "x$ac_cv_have_decl_TCP_NOTSENT_LOWAT" = xyes; then : @@ -8249,6 +8299,17 @@ cat >>confdefs.h <<_ACEOF #define HAVE_DECL_TCP_NOTSENT_LOWAT $ac_have_decl _ACEOF +ac_fn_c_check_decl "$LINENO" "TCP_MAXSEG" "ac_cv_have_decl_TCP_MAXSEG" "$in_h +" +if test "x$ac_cv_have_decl_TCP_MAXSEG" = xyes; then : + ac_have_decl=1 +else + ac_have_decl=0 +fi + +cat >>confdefs.h <<_ACEOF +#define HAVE_DECL_TCP_MAXSEG $ac_have_decl +_ACEOF ac_fn_c_check_decl "$LINENO" "IP_ADD_MEMBERSHIP" "ac_cv_have_decl_IP_ADD_MEMBERSHIP" "$in_h " if test "x$ac_cv_have_decl_IP_ADD_MEMBERSHIP" = xyes; then : @@ -8988,6 +9049,11 @@ $as_echo "#define HAVE_FASTSAMPLING 1" >>confdefs.h fi +if test "$enable_discover_defaultlen" = yes; then + +$as_echo "#define DEFAULT_PAYLOAD_LEN_PER_MTU_DISCOVERY 1" >>confdefs.h + +fi if test "$enable_packet_debug" = yes; then @@ -9157,6 +9223,10 @@ as_fn_error $? "conditional \"FASTSAMPLING\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi +if test -z "${DISCOVER_DEFAULTLEN_TRUE}" && test -z "${DISCOVER_DEFAULTLEN_FALSE}"; then + as_fn_error $? "conditional \"DISCOVER_DEFAULTLEN\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi if test -z "${THREAD_DEBUG_TRUE}" && test -z "${THREAD_DEBUG_FALSE}"; then as_fn_error $? "conditional \"THREAD_DEBUG\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 @@ -9603,7 +9673,7 @@ # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by Iperf $as_me 2.1.5, which was +This file was extended by Iperf $as_me 2.1.7, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -9669,7 +9739,7 @@ cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -Iperf config.status 2.1.5 +Iperf config.status 2.1.7 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" diff -Nru iperf-2.1.5+dfsg1/configure.ac iperf-2.1.7+dfsg1/configure.ac --- iperf-2.1.5+dfsg1/configure.ac 2021-12-05 20:50:04.000000000 +0000 +++ iperf-2.1.7+dfsg1/configure.ac 2022-04-11 18:14:01.000000000 +0000 @@ -5,7 +5,7 @@ AC_PREREQ(2.59) -AC_INIT(Iperf,2.1.5) +AC_INIT(Iperf,2.1.7) AC_CONFIG_HEADER([config.h]) AM_INIT_AUTOMAKE() @@ -55,6 +55,10 @@ [enable support for 100 microsecond report intervals (default is disable)])) AM_CONDITIONAL([FASTSAMPLING], [test "x$enable_fastsampling" = "xyes"]) +AC_ARG_ENABLE(discover_defaultlen, AC_HELP_STRING([--enable-discover-defaultlen], + [enable support to set the default payload size after device MTU discovery (default is disable)])) +AM_CONDITIONAL([DISCOVER_DEFAULTLEN], [test "x$enable_discover_defaultlen" = "xyes"]) + AC_ARG_ENABLE(thread_debug, AC_HELP_STRING([--enable-thread-debug], [enable support for thread debugging (default is disable)])) AM_CONDITIONAL([THREAD_DEBUG], [test "x$enable_thread_debug" = "xyes"]) @@ -171,7 +175,7 @@ dnl Checks for header files. AC_HEADER_STDC -AC_CHECK_HEADERS([arpa/inet.h libintl.h net/ethernet.h net/if.h linux/ip.h linux/udp.h linux/if_packet.h linux/filter.h linux/if_tun.h netdb.h netinet/in.h netinet/tcp.h stdlib.h string.h strings.h sys/socket.h sys/time.h syslog.h unistd.h signal.h ifaddrs.h]) +AC_CHECK_HEADERS([arpa/inet.h libintl.h net/ethernet.h net/if.h sys/ioctl.h sys/sockio.h linux/sockios.h linux/ip.h linux/udp.h linux/if_packet.h linux/filter.h linux/if_tun.h netdb.h netinet/in.h netinet/tcp.h stdlib.h string.h strings.h sys/socket.h sys/time.h syslog.h unistd.h signal.h ifaddrs.h]) dnl =================================================================== dnl Checks for typedefs, structures @@ -217,6 +221,14 @@ AC_CHECK_FUNCS([atexit memset select strchr strerror strtol strtoll usleep clock_gettime sched_setscheduler sched_yield mlockall setitimer nanosleep clock_nanosleep freopen]) AC_REPLACE_FUNCS(snprintf inet_pton inet_ntop gettimeofday) AC_CHECK_DECLS([ENOBUFS, EWOULDBLOCK],[],[],[#include ]) +AC_CHECK_DECLS([SIOCGIFMTU],[],[],[ +#ifdef HAVE_LINUX_SOCKIOS_H +#include +#endif +#ifdef HAVE_SYS_SOCKIO_H +#include +#endif +]) AC_CHECK_DECLS([pthread_cancel],[],[],[#include ]) AC_CHECK_DECLS([CPU_SET],[],[],[ #define _GNU_SOURCE @@ -243,8 +255,9 @@ SO_MAX_PACING_RATE, SO_DONTROUTE, IPV6_TCLASS, IP_MULTICAST_ALL, IP_TOS, MCAST_JOIN_GROUP, MCAST_JOIN_SOURCE_GROUP, IPV6_JOIN_GROUP, IPV6_V6ONLY, IPV6_ADD_MEMBERSHIP, IPV6_MULTICAST_HOPS, MSG_PEEK, MSG_WAITALL, - TCP_NODELAY, TCP_INFO, TCP_CONNECTION_INFO, TCP_WINDOW_CLAMP, - TCP_NOTSENT_LOWAT, IP_ADD_MEMBERSHIP, IP_ADD_SOURCE_MEMBERSHIP],[],[],[$in_h]) + TCP_NODELAY, TCP_INFO, TCP_CONNECTION_INFO, TCP_WINDOW_CLAMP, TCP_QUICKACK, + TCP_NOTSENT_LOWAT, TCP_MAXSEG, IP_ADD_MEMBERSHIP, IP_ADD_SOURCE_MEMBERSHIP], + [],[],[$in_h]) AC_CHECK_TYPES([struct sockaddr_storage, struct sockaddr_in6, struct group_source_req, struct ip_mreq, @@ -403,6 +416,9 @@ if test "$enable_fastsampling" = yes; then AC_DEFINE([HAVE_FASTSAMPLING], 1, [Define if fast sampling for report intervals is desired]) fi +if test "$enable_discover_defaultlen" = yes; then +AC_DEFINE([DEFAULT_PAYLOAD_LEN_PER_MTU_DISCOVERY], 1, [Define if default UDP payload length is based on device MTU and socket buffer size]) +fi if test "$enable_packet_debug" = yes; then AC_DEFINE([HAVE_PACKET_DEBUG], 1, [Define if packet level debugging is desired]) diff -Nru iperf-2.1.5+dfsg1/debian/changelog iperf-2.1.7+dfsg1/debian/changelog --- iperf-2.1.5+dfsg1/debian/changelog 2021-12-09 23:47:56.000000000 +0000 +++ iperf-2.1.7+dfsg1/debian/changelog 2022-04-27 15:00:23.000000000 +0000 @@ -1,3 +1,9 @@ +iperf (2.1.7+dfsg1-1) unstable; urgency=low + + * New upstream version. + + -- Roberto Lumbreras Wed, 27 Apr 2022 17:00:23 +0200 + iperf (2.1.5+dfsg1-1) unstable; urgency=low * New upstream version. diff -Nru iperf-2.1.5+dfsg1/debian/patches/003-fix-hyphen-used-as-minus-sign.patch iperf-2.1.7+dfsg1/debian/patches/003-fix-hyphen-used-as-minus-sign.patch --- iperf-2.1.5+dfsg1/debian/patches/003-fix-hyphen-used-as-minus-sign.patch 2021-12-09 14:26:50.000000000 +0000 +++ iperf-2.1.7+dfsg1/debian/patches/003-fix-hyphen-used-as-minus-sign.patch 2022-04-27 15:00:23.000000000 +0000 @@ -1,8 +1,10 @@ Description: fix hyphen used as minus sign in manpages Author: Roberto Lumbreras ---- a/man/iperf.1 -+++ b/man/iperf.1 +Index: iperf-2.1.7+dfsg1/man/iperf.1 +=================================================================== +--- iperf-2.1.7+dfsg1.orig/man/iperf.1 2022-04-27 17:01:38.400965159 +0200 ++++ iperf-2.1.7+dfsg1/man/iperf.1 2022-04-27 17:11:46.637963105 +0200 @@ -2,13 +2,13 @@ .SH NAME iperf \- perform network traffic tests using network sockets. Metrics include throughput and latency. @@ -32,7 +34,7 @@ available metrics. .LP The user must establish both a both a server (to receive traffic) -@@ -26,273 +26,273 @@ +@@ -26,291 +26,291 @@ computers but need not be. .SH "GENERAL OPTIONS" .TP @@ -73,9 +75,8 @@ perform layer 2 length checks on received UDP packets (requires systems that support packet sockets, e.g. Linux) .TP -.BR -m ", " --print_mss " " --print TCP maximum segment size (MTU - TCP/IP header) +.BR \-m ", " \-\-print_mss " " -+print TCP maximum segment size (MTU \- TCP/IP header) + print TCP maximum segment size .TP -.BR " --NUM_REPORT_STRUCTS " \fI\fR +.BR " \-\-NUM_REPORT_STRUCTS " \fI\fR @@ -130,9 +131,8 @@ for use with older versions does not sent extra msgs .TP -.BR -M ", " --mss " \fIn\fR" --set TCP maximum segment size (MTU - 40 bytes) +.BR \-M ", " \-\-mss " \fIn\fR" -+set TCP maximum segment size (MTU \- 40 bytes) + set TCP maximum segment size using TCP_MAXSEG .TP -.BR -N ", " --nodelay " " +.BR \-N ", " \-\-nodelay " " @@ -196,7 +196,7 @@ .TP -.BR " --tos-override " \fIn\fR +.BR " \-\-tos\-override " \fIn\fR - set the socket's IP_TOS (byte) field for reverse or full duplex traffic. Supported in versions 2.1.5 or greater. Previous versions won't set IP_TOSq on reverse traffic. + set the socket's IP_TOS (byte) field for reverse or full duplex traffic. Supported in versions 2.1.5 or greater. Previous versions won't set IP_TOS on reverse traffic. .TP -.BR -B ", " --bind " \fIip\fR | \fIip\fR%\fIdevice\fR" +.BR \-B ", " \-\-bind " \fIip\fR | \fIip\fR%\fIdevice\fR" @@ -235,9 +235,27 @@ set target bandwidth to \fIn\fR bits/sec (default 1 Mbit/sec) or \fIn\fR packets per sec. This may be used with TCP or UDP. Optionally, for variable loads, use format of mean,standard deviation .TP --.BR -c ", " --client " \fI\fIhost\fR | \fIhost\fR%\fIdevice\fR" -+.BR \-c ", " \-\-client " \fI\fIhost\fR | \fIhost\fR%\fIdevice\fR" - run in client mode, connecting to \fIhost\fR where the optional %dev will SO_BINDTODEVICE that output interface (requires root and see NOTES) +-.BR " --bounceback " +-run a tcp bounceback test (set size with -l or --len, defaults to 100 bytes) ++.BR " \-\-bounceback " ++run a tcp bounceback test (set size with \-l or \-\-len, defaults to 100 bytes) + .TP +-.BR " --bounceback-congest " +-request a concurrent full-duplex TCP stream ++.BR " \-\-bounceback\-congest " ++request a concurrent full\-duplex TCP stream + .TP +-.BR " --bounceback-hold " \fIn\fR ++.BR " \-\-bounceback\-hold " \fIn\fR + request the server to insert a delay of n milliseconds between its read and write (default is no delay) + .TP +-.BR " --bounceback-period " \fIn\fR ++.BR " \-\-bounceback\-period " \fIn\fR + request the client schedule a send every n milliseconds (w/o option bouncebacks are immediately scheduled. With command option and no optional value given, the interval will be one second.) + .TP +-.BR " --bounceback-no-quickack " ++.BR " \-\-bounceback\-no\-quickack " + request the server not set the TCP_QUICKACK socket option (disabling TCP ACK delays) during a bounceback test (see NOTES) .TP -.BR " --burst-period " \fIn\fR +.BR " \-\-burst\-period " \fIn\fR @@ -246,6 +264,10 @@ -.BR " --burst-size " \fIn\fR +.BR " \-\-burst\-size " \fIn\fR Set the burst size in bytes. Defaults to 1M if no value is given. +-.BR -c ", " --client " \fI\fIhost\fR | \fIhost\fR%\fIdevice\fR" ++.BR \-c ", " \-\-client " \fI\fIhost\fR | \fIhost\fR%\fIdevice\fR" + run in client mode, connecting to \fIhost\fR where the optional %dev will SO_BINDTODEVICE that output interface (requires root and see NOTES) + .TP .TP -.BR " --connect-only[=" \fIn\fR "]" -only perform a TCP connect (or 3WHS) without any data transfer, useful to measure TCP connect() times. Optional value of n is the total number of connects to do (zero is run forever.) Note that -i will rate limit the connects where -P will create bursts and -t will end the client and hence end its connect attempts. @@ -344,15 +366,18 @@ +Do a bidirectional test individually \- client\-to\-server, followed by +a reversed test, server\-to\-client .TP --.BR " --tcp-drain " --This is an experimental feature to measure the sending (client) host's sojourn times. Measure delay after completion of writing a burst (set via -l or --burst-size) and when TCP_NOTSENT_LOWAT set to a small value triggers the select() indicating all bytes per the burst are inflight. Output is a D8 histogram on the client side. -+.BR " \-\-tcp\-drain " -+This is an experimental feature to measure the sending (client) host's sojourn times. Measure delay after completion of writing a burst (set via \-l or \-\-burst\-size) and when TCP_NOTSENT_LOWAT set to a small value triggers the select() indicating all bytes per the burst are inflight. Output is a D8 histogram on the client side. +-.BR " --tcp-quickack " ++.BR " \-\-tcp\-quickack " + Set TCP_QUICKACK on the socket .TP -.BR " --tcp-write-prefetch " \fIn\fR[kmKM] +.BR " \-\-tcp\-write\-prefetch " \fIn\fR[kmKM] Set TCP_NOTSENT_LOWAT on the socket and use event based writes per select() on the socket. .TP +-.BR " --tcp-write-times " ++.BR " \-\-tcp\-write\-times " + Measure the socket write times + .TP -.BR -t ", " --time " \fIn\fR" | "\fI0\fR" +.BR \-t ", " \-\-time " \fIn\fR" | "\fI0\fR" time in seconds to transmit traffic, use zero for infinite (default is 10 secs) @@ -423,7 +448,7 @@ .br ------------------------------------------------------------ .br -@@ -308,35 +308,35 @@ +@@ -326,35 +326,35 @@ .br [ ID] Interval Transfer Bandwidth Write/Err Rtry Cwnd/RTT NetPwr .br @@ -472,7 +497,7 @@ .br .B Rtry Total number of TCP retries -@@ -352,7 +352,7 @@ +@@ -370,7 +370,7 @@ .B TCP tests (server) .B @@ -481,7 +506,7 @@ .br ------------------------------------------------------------ .br -@@ -368,43 +368,43 @@ +@@ -386,43 +386,43 @@ .br [ ID] Interval Transfer Bandwidth Reads Dist(bin=1.0K) .br @@ -540,7 +565,7 @@ .br ------------------------------------------------------------ .br -@@ -414,35 +414,35 @@ +@@ -432,35 +432,35 @@ .br ------------------------------------------------------------ .br @@ -590,7 +615,7 @@ .B Burst Latency One way TCP write() to read() latency in mean/minimum/maximum/standard deviation format (Note: requires the client's and server's system clocks to be -@@ -457,16 +457,16 @@ +@@ -475,16 +475,16 @@ .br .B inP inP, short for in progress, is the average number of bytes in progress or in flight. This is taken from the application level write to read perspective. Note this is @@ -610,7 +635,7 @@ .br ------------------------------------------------------------ .br -@@ -478,26 +478,26 @@ +@@ -496,26 +496,26 @@ .br ------------------------------------------------------------ .br @@ -644,7 +669,7 @@ .br ------------------------------------------------------------ .br -@@ -513,36 +513,36 @@ +@@ -531,36 +531,36 @@ .br [ ID] Interval Transfer Bandwidth Write/Err PPS .br @@ -694,7 +719,7 @@ .br .B PPS Transmit packet rate in packets per second -@@ -550,7 +550,7 @@ +@@ -568,7 +568,7 @@ .PP .B UDP tests (server) @@ -703,7 +728,7 @@ .br ------------------------------------------------------------ .br -@@ -562,37 +562,37 @@ +@@ -580,37 +580,37 @@ .br ------------------------------------------------------------ .br @@ -755,7 +780,7 @@ .B Latency End to end latency in mean/minimum/maximum/standard deviation format (Note: requires the client's and server's system clocks to be -@@ -603,7 +603,7 @@ +@@ -621,7 +621,7 @@ Received packet rate in packets per second .br .B inP @@ -764,7 +789,7 @@ .br .B NetPwr Network power defined as (throughput / latency) -@@ -612,7 +612,7 @@ +@@ -630,7 +630,7 @@ .B Isochronous UDP tests (client) @@ -773,7 +798,7 @@ .br ------------------------------------------------------------ .br -@@ -628,36 +628,36 @@ +@@ -646,36 +646,36 @@ .br [ ID] Interval Transfer Bandwidth Write/Err PPS frames:tx/missed/slips .br @@ -823,7 +848,7 @@ .B frames:tx/missed/slips Total number of isochronous frames or bursts. Total number of frame ids not sent. Total number of frame slips -@@ -665,7 +665,7 @@ +@@ -683,7 +683,7 @@ .B Isochronous UDP tests (server) @@ -832,7 +857,7 @@ .br ------------------------------------------------------------ .br -@@ -677,26 +677,26 @@ +@@ -695,26 +695,26 @@ .br ------------------------------------------------------------ .br @@ -845,7 +870,7 @@ +[ 3] 0.00\-9.98 sec 120 MBytes 101 Mbits/sec 0.010 ms 196/85867 (0.23%) 0.665/ 0.083/ 1.318/ 0.284 ms 8585 pps 18903.85 601/1 .br -[ 3] 0.00-9.98 sec T8(f)-PDF: bin(w=100us):cnt(85671)=1:2,2:844,3:10034,4:8493,5:8967,6:8733,7:8823,8:9023,9:8901,10:8816,11:7730,12:4563,13:741,14:1 (5.00/95.00%=3/12,Outliers=0,obl/obu=0/0) -+[ 3] 0.00\-9.98 sec T8(f)-PDF: bin(w=100us):cnt(85671)=1:2,2:844,3:10034,4:8493,5:8967,6:8733,7:8823,8:9023,9:8901,10:8816,11:7730,12:4563,13:741,14:1 (5.00/95.00%=3/12,Outliers=0,obl/obu=0/0) ++[ 3] 0.00\-9.98 sec T8(f)\-PDF: bin(w=100us):cnt(85671)=1:2,2:844,3:10034,4:8493,5:8967,6:8733,7:8823,8:9023,9:8901,10:8816,11:7730,12:4563,13:741,14:1 (5.00/95.00%=3/12,Outliers=0,obl/obu=0/0) .br -[ 3] 0.00-9.98 sec F8(f)-PDF: bin(w=100us):cnt(598)=15:2,16:1,17:27,18:68,19:125,20:136,21:103,22:83,23:22,24:23,25:5,26:3 (5.00/95.00%=17/24,Outliers=0,obl/obu=0/0) +[ 3] 0.00\-9.98 sec F8(f)\-PDF: bin(w=100us):cnt(598)=15:2,16:1,17:27,18:68,19:125,20:136,21:103,22:83,23:22,24:23,25:5,26:3 (5.00/95.00%=17/24,Outliers=0,obl/obu=0/0) @@ -857,7 +882,8 @@ +Total number of frames (or bursts) received. Total number of bursts lost or error\-ed .br .B - T8-PDF(f) +-T8-PDF(f) ++T8\-PDF(f) Latency histogram for packets .br -.B F8-PDF(f) @@ -865,7 +891,7 @@ Latency histogram for frames -@@ -715,92 +715,92 @@ +@@ -733,106 +733,106 @@ based, e.g. 1k = 1000, 1K = 1024, 1m = 1,000,000 and 1M = 1,048,576 .P .B Rate limiting: @@ -965,6 +991,24 @@ -directly rate limit the writes with TCP testing when using --reverse. +directly rate limit the writes with TCP testing when using \-\-reverse. .P + .B Bounceback + The bounceback test allows one to measure network responsiveness (which, in this test, is an inverse of latency.) + Latency is merely delay in units of time. Latency metrics require one to know the delay of what's being measured. + For bounceback it's a client write to a server read followed by a server write and then the client read. The original + write is bounce backed. Iperf 2 sets up the socket with TCP_NODELAY and possibly TCP_QUICKACK (unless disabled). +-The client sends a small write (which defaults to 100 bytes unless -l is set) and issues a read waiting for the "bounceback" ++The client sends a small write (which defaults to 100 bytes unless \-l is set) and issues a read waiting for the "bounceback" + from the server. The server waits for a read and then optionally delays before sending the payload back. + This repeats until the traffic ends. + .P +-The TCP_QUICKACK socket option will be enabled during bounceback tests when the bounceback-hold +-is set to a non-zero value. The socket option is applied after every read() on the server +-and before the hold delay call. It's also applied on the client. Use --bounceback-no-quickack ++The TCP_QUICKACK socket option will be enabled during bounceback tests when the bounceback\-hold ++is set to a non\-zero value. The socket option is applied after every read() on the server ++and before the hold delay call. It's also applied on the client. Use \-\-bounceback\-no\-quickack + to disable TCP ack delays per the socket. + .P .B TCP Connect times: The TCP connect time (or three way handshake) can be seen on the iperf -client when the -e (--enhanced) option is set. Look for the @@ -992,7 +1036,7 @@ .P \fBLittle's Law\fR in queuing theory is a theorem that determines the average number of items (L) in a stationary queuing system based on the average waiting time (W) of an item within a system and the average number of items arriving at the system per unit of time (lambda). Mathematically, it's L = lambda * W. As used here, the units are bytes. The arrival rate is taken from the writes. .P -@@ -812,20 +812,20 @@ +@@ -844,23 +844,23 @@ For UDP the delay is the end/end latency. Don't confuse this with the physics definition of power (delta energy/delta time) but more of a measure of a desirable property @@ -1005,6 +1049,9 @@ -Iperf 2 supports multicast with a couple of caveats. First, multicast streams cannot take advantage of the -P option. The server will serialize multicast streams. Also, it's highly encouraged to use a -t on a server that will be used for multicast clients. That is because the single end of traffic packet sent from client to server may get lost and there are no redundant end of traffic packets. Setting -t on the server will kill the server thread in the event this packet is indeed lost. +Iperf 2 supports multicast with a couple of caveats. First, multicast streams cannot take advantage of the \-P option. The server will serialize multicast streams. Also, it's highly encouraged to use a \-t on a server that will be used for multicast clients. That is because the single end of traffic packet sent from client to server may get lost and there are no redundant end of traffic packets. Setting \-t on the server will kill the server thread in the event this packet is indeed lost. .P + .B TCP_QUICACK: + The TCP_QUICKACK socket applied after every read() on the server + .P .B Fast Sampling: Use -.B ./configure --enable-fastsampling @@ -1018,7 +1065,7 @@ and then compile from source to enable both asserts and advanced debugging of the tool itself. .SH BUGS See https://sourceforge.net/p/iperf2/tickets/ -@@ -837,7 +837,7 @@ +@@ -872,7 +872,7 @@ Kevin Gibbs, John Estabrook , Andrew Gallatin , diff -Nru iperf-2.1.5+dfsg1/debian/patches/004-fix-man-break-lines.patch iperf-2.1.7+dfsg1/debian/patches/004-fix-man-break-lines.patch --- iperf-2.1.5+dfsg1/debian/patches/004-fix-man-break-lines.patch 2021-12-09 14:31:29.000000000 +0000 +++ iperf-2.1.7+dfsg1/debian/patches/004-fix-man-break-lines.patch 2022-04-27 15:00:23.000000000 +0000 @@ -1,14 +1,16 @@ Description: fix cannot break lines error Author: Roberto Lumbreras ---- a/man/iperf.1 -+++ b/man/iperf.1 -@@ -683,9 +683,9 @@ +Index: iperf-2.1.7+dfsg1/man/iperf.1 +=================================================================== +--- iperf-2.1.7+dfsg1.orig/man/iperf.1 2022-04-27 17:12:46.169662916 +0200 ++++ iperf-2.1.7+dfsg1/man/iperf.1 2022-04-27 17:13:45.589363733 +0200 +@@ -701,9 +701,9 @@ .br [ 3] 0.00\-9.98 sec 120 MBytes 101 Mbits/sec 0.010 ms 196/85867 (0.23%) 0.665/ 0.083/ 1.318/ 0.284 ms 8585 pps 18903.85 601/1 .br --[ 3] 0.00\-9.98 sec T8(f)-PDF: bin(w=100us):cnt(85671)=1:2,2:844,3:10034,4:8493,5:8967,6:8733,7:8823,8:9023,9:8901,10:8816,11:7730,12:4563,13:741,14:1 (5.00/95.00%=3/12,Outliers=0,obl/obu=0/0) -+[ 3] 0.00\-9.98 sec T8(f)-PDF: bin(w=100us):cnt(85671)= 1:2,2:844,3:10034,4:8493,5:8967,6:8733,7:8823,8:9023,9:8901,10:8816,11:7730,12:4563,13:741,14:1 (5.00/95.00%=3/12,Outliers=0,obl/obu=0/0) +-[ 3] 0.00\-9.98 sec T8(f)\-PDF: bin(w=100us):cnt(85671)=1:2,2:844,3:10034,4:8493,5:8967,6:8733,7:8823,8:9023,9:8901,10:8816,11:7730,12:4563,13:741,14:1 (5.00/95.00%=3/12,Outliers=0,obl/obu=0/0) ++[ 3] 0.00\-9.98 sec T8(f)\-PDF: bin(w=100us):cnt(85671)= 1:2,2:844,3:10034,4:8493,5:8967,6:8733,7:8823,8:9023,9:8901,10:8816,11:7730,12:4563,13:741,14:1 (5.00/95.00%=3/12,Outliers=0,obl/obu=0/0) .br -[ 3] 0.00\-9.98 sec F8(f)\-PDF: bin(w=100us):cnt(598)=15:2,16:1,17:27,18:68,19:125,20:136,21:103,22:83,23:22,24:23,25:5,26:3 (5.00/95.00%=17/24,Outliers=0,obl/obu=0/0) +[ 3] 0.00\-9.98 sec F8(f)\-PDF: bin(w=100us):cnt(598)= 15:2,16:1,17:27,18:68,19:125,20:136,21:103,22:83,23:22,24:23,25:5,26:3 (5.00/95.00%=17/24,Outliers=0,obl/obu=0/0) diff -Nru iperf-2.1.5+dfsg1/debian/patches/016_configure-g.patch iperf-2.1.7+dfsg1/debian/patches/016_configure-g.patch --- iperf-2.1.5+dfsg1/debian/patches/016_configure-g.patch 2021-12-09 14:33:52.000000000 +0000 +++ iperf-2.1.7+dfsg1/debian/patches/016_configure-g.patch 2022-04-27 15:00:23.000000000 +0000 @@ -2,9 +2,11 @@ fix configure deleting -g flag from *FLAGS Author: Roberto Lumbreras ---- a/configure.ac -+++ b/configure.ac -@@ -131,9 +131,7 @@ +Index: iperf-2.1.7+dfsg1/configure.ac +=================================================================== +--- iperf-2.1.7+dfsg1.orig/configure.ac 2022-04-27 17:14:20.165189862 +0200 ++++ iperf-2.1.7+dfsg1/configure.ac 2022-04-27 17:14:20.161189882 +0200 +@@ -135,9 +135,7 @@ dnl =================================================================== AC_PROG_CXX diff -Nru iperf-2.1.5+dfsg1/debian/patches/017-spelling-errors iperf-2.1.7+dfsg1/debian/patches/017-spelling-errors --- iperf-2.1.5+dfsg1/debian/patches/017-spelling-errors 2021-12-09 14:39:14.000000000 +0000 +++ iperf-2.1.7+dfsg1/debian/patches/017-spelling-errors 2022-04-27 15:00:23.000000000 +0000 @@ -2,20 +2,34 @@ fix spelling errors / typos Author: Roberto Lumbreras ---- a/src/socket_io.c -+++ b/src/socket_io.c -@@ -265,7 +265,7 @@ +Index: iperf-2.1.7+dfsg1/src/socket_io.c +=================================================================== +--- iperf-2.1.7+dfsg1.orig/src/socket_io.c 2022-04-27 17:15:18.056899102 +0200 ++++ iperf-2.1.7+dfsg1/src/socket_io.c 2022-04-27 17:15:56.836704566 +0200 +@@ -208,7 +208,7 @@ case SOCKET_ERROR : if (!(errno == EINTR) || (errno == EAGAIN) || (errno == EWOULDBLOCK)) { nwritten = inLen - nleft; - WARN_errno(1, "writen fatal"); + WARN_errno(1, "written fatal"); + sInterupted = 1; goto DONE; } - break; ---- a/man/iperf.1 -+++ b/man/iperf.1 -@@ -730,7 +730,7 @@ +Index: iperf-2.1.7+dfsg1/man/iperf.1 +=================================================================== +--- iperf-2.1.7+dfsg1.orig/man/iperf.1 2022-04-27 17:15:18.056899102 +0200 ++++ iperf-2.1.7+dfsg1/man/iperf.1 2022-04-27 17:24:24.990150195 +0200 +@@ -63,7 +63,8 @@ + output the report or error message to this specified file + .TP + .BR " \-\-permit\-key [=" \fI\fR "]" +-Set a key value that must match for the server to accept traffic on a connection. If the option is given without a value on the server a key value will be autogenerated and displayed in its initial settings report. The lifetime of the key is set using \-\-permit\-key\-timeout and defaults to twenty seconds. The value is required on clients. The value will also be used as part of the transfer id in reports. The option set on the client but not the server will also cause the server to reject the client's traffic. TCP only, no UDP support. ++Set a key value that must match for the server to accept traffic on a connection. If the option is given without a value on the server a key value will be autogenerated and displayed in its initial settings report. The lifetime of the key is set using \-\-permit\-key\-timeout and defaults to twenty seconds. The value is required on clients. The value will also be used as part of the transfer id in reports. ++The option set on the client but not the server will also cause the server to reject the client's traffic. TCP only, no UDP support. + .TP + .BR \-p ", " \-\-port " \fIm\fR[\-\fIn\fR]" + set client or server port(s) to send or listen on per \fIm\fR (default 5001) w/optional port range per m\-n (e.g. \-p 6002\-6008) (see NOTES) +@@ -748,7 +749,7 @@ .P .B Histograms and non\-parametric statisitics: The \-\-histograms option provides the raw data where nothing is averaged. This is useful for non\-parametric @@ -24,7 +38,7 @@ mininimum, maximum and variation. This loses information when the underlining distribution is not gaussian. Histograms are supported so this information is made available. .P -@@ -738,7 +738,7 @@ +@@ -756,7 +757,7 @@ Binding is done at the logical level of port and ip address (or layer 3) using the \-B option and a colon as the separator between port and the ip addr. Binding at the @@ -33,9 +47,21 @@ An example for src port and ip address is \-B 192.168.1.1:6001. To \fBbind the src port only\fR and let the operating system choose the source ip address use 0.0.0.0, e.g. \fB\-B 0.0.0.0:6001\fR. On the client, the \-B option affects the \fBbind\fR(2) ---- a/src/Client.cpp -+++ b/src/Client.cpp -@@ -935,7 +935,7 @@ +@@ -823,7 +824,8 @@ + shows the 3WHS took 1.84 milliseconds. + .P + .B Port\-range +-Port ranges are supported using the hyphen notation, e.g. 6001\-6009. This will cause multiple threads, one per port, on either the listener/server or the client. The user needs to take care that the ports in the port range are available and not already in use per the operating system. The \-P is supported on the client and will apply to each destination port within the port range. Finally, this can be used for a workaround for Windows UDP and \-P > 1 as Windows doesn't dispatch UDP per a server's connect and the quintuple. ++Port ranges are supported using the hyphen notation, e.g. 6001\-6009. This will cause multiple threads, one per port, on either the listener/server or the client. The user needs to take care that the ports in the port range are available and not already in use per the operating system. The \-P is supported on the client and will apply to each destination port within the port range. ++Finally, this can be used for a workaround for Windows UDP and \-P > 1 as Windows doesn't dispatch UDP per a server's connect and the quintuple. + .P + .B Packet per second (pps) calculation + The packets per second calculation is done as a derivative, i.e. number of packets divided by +Index: iperf-2.1.7+dfsg1/src/Client.cpp +=================================================================== +--- iperf-2.1.7+dfsg1.orig/src/Client.cpp 2022-04-27 17:15:18.056899102 +0200 ++++ iperf-2.1.7+dfsg1/src/Client.cpp 2022-04-27 17:15:18.056899102 +0200 +@@ -939,7 +939,7 @@ reportstruct->sentTime = reportstruct->packetTime; reportstruct->packetLen = writen(mySocket, mSettings->mBuf, writelen, &reportstruct->writecnt); if (reportstruct->packetLen <= 0) { diff -Nru iperf-2.1.5+dfsg1/include/Client.hpp iperf-2.1.7+dfsg1/include/Client.hpp --- iperf-2.1.5+dfsg1/include/Client.hpp 2021-12-05 20:49:56.000000000 +0000 +++ iperf-2.1.7+dfsg1/include/Client.hpp 2022-04-11 18:13:03.000000000 +0000 @@ -88,6 +88,7 @@ private: inline void WritePacketID(intmax_t); inline void WriteTcpTxHdr(struct ReportStruct *, int, int); + inline void WriteTcpTxBBHdr(struct ReportStruct *, int); inline double get_delay_target(void); void InitTrafficLoop(void); void SetReportStartTime(void); @@ -98,7 +99,6 @@ void PostNullEvent(void); void AwaitServerCloseEvent(void); inline void tcp_shutdown(void); - inline void tcp_drain(void); bool connected; ReportStruct scratchpad; ReportStruct *reportstruct; @@ -139,9 +139,7 @@ Isochronous::FrameCounter *framecounter; bool isburst; bool peerclose; - Timestamp drain_start; - Timestamp drain_end; - struct tcp_init_conditions my_init_cond; + Timestamp write_start; }; // end class Client #endif // CLIENT_H diff -Nru iperf-2.1.5+dfsg1/include/gettcpinfo.h iperf-2.1.7+dfsg1/include/gettcpinfo.h --- iperf-2.1.5+dfsg1/include/gettcpinfo.h 2021-12-05 20:49:56.000000000 +0000 +++ iperf-2.1.7+dfsg1/include/gettcpinfo.h 2022-04-11 18:13:03.000000000 +0000 @@ -49,18 +49,31 @@ #define GETTCPINFO_H #include "headers.h" -#include "Reporter.h" -#include "packet_ring.h" #ifdef __cplusplus extern "C" { #endif +struct iperf_tcpstats { + bool isValid; + int rtt; + double connecttime; +#if HAVE_TCP_STATS + int cwnd; + int rttvar; + intmax_t retry; + intmax_t retry_prev; + intmax_t retry_tot; + int mss_negotiated; +#endif +}; + #if WIN32 -void gettcpinfo(SOCKET sock, struct ReportStruct *sample); +void gettcpinfo(SOCKET sock, struct iperf_tcpstats *sample); #else -void gettcpinfo(int sock, struct ReportStruct *sample); +void gettcpinfo(int sock, struct iperf_tcpstats *sample); #endif +void tcpstats_copy (struct iperf_tcpstats *stats_dst, struct iperf_tcpstats *stats_src); #ifdef __cplusplus } /* end extern "C" */ diff -Nru iperf-2.1.5+dfsg1/include/headers.h iperf-2.1.7+dfsg1/include/headers.h --- iperf-2.1.5+dfsg1/include/headers.h 2021-12-05 20:49:56.000000000 +0000 +++ iperf-2.1.7+dfsg1/include/headers.h 2022-04-11 18:13:03.000000000 +0000 @@ -102,16 +102,30 @@ #endif +// v4: 1470 bytes UDP payload will fill one and only one ethernet datagram (IPv4 overhead is 20 bytes) +#define kDefault_UDPBufLen 1470 +// v6: 1450 bytes UDP payload will fill one and only one ethernet datagram (IPv6 overhead is 40 bytes) +#define kDefault_UDPBufLenV6 1450 +#define IPV4HDRLEN 20 +#define IPV6HDRLEN 40 +#define UDPHDRLEN 8 + #if ((defined HAVE_SSM_MULTICAST) || (defined HAVE_DECL_SO_BINDTODEVICE)) && (defined HAVE_NET_IF_H) #include #endif - +#ifdef HAVE_SYS_IOCTL_H +#include +#endif +#ifdef HAVE_SYS_SOCKIO_H +#include +#endif +#ifdef HAVE_LINUX_SOCKIOS_H +#include +#endif #if ((HAVE_TUNTAP_TAP) || (HAVE_TUNTAP_TUN)) #include -#include #endif - // AF_PACKET HEADERS #if defined(HAVE_LINUX_FILTER_H) && defined(HAVE_AF_PACKET) #include @@ -143,7 +157,7 @@ // #include // // force the ipv6hdr length to 40 vs use of sizeof(struct ipv6hdr) -#define IPV6HDRLEN 40 + #endif // HAVE_AF_PACKET #ifdef WIN32 diff -Nru iperf-2.1.5+dfsg1/include/Locale.h iperf-2.1.7+dfsg1/include/Locale.h --- iperf-2.1.5+dfsg1/include/Locale.h 2021-12-05 20:49:56.000000000 +0000 +++ iperf-2.1.7+dfsg1/include/Locale.h 2022-04-11 18:13:03.000000000 +0000 @@ -140,6 +140,8 @@ extern const char client_bounceback[]; +extern const char client_bounceback_noqack[]; + extern const char server_burstperiod[]; extern const char client_fq_pacing[]; @@ -211,15 +213,15 @@ extern const char report_bw_write_enhanced_header[]; -extern const char report_sumcnt_write_enhanced_drain_header[]; +extern const char report_sumcnt_write_enhanced_write_header[]; extern const char report_bw_write_enhanced_format[]; -extern const char report_write_enhanced_drain_header[]; +extern const char report_write_enhanced_write_header[]; -extern const char report_write_enhanced_drain_format[]; +extern const char report_write_enhanced_write_format[]; -extern const char report_write_enhanced_nocwnd_drain_format[]; +extern const char report_write_enhanced_nocwnd_write_format[]; extern const char report_bw_write_enhanced_nocwnd_format[]; @@ -283,6 +285,14 @@ extern const char report_burst_read_tcp_final_format[]; +extern const char report_burst_write_tcp_header[]; + +extern const char report_burst_write_tcp_format[]; + +extern const char report_burst_write_tcp_nocwnd_format[]; + +extern const char report_burst_write_tcp_final_format[]; + extern const char report_udp_fullduplex_header[]; extern const char report_udp_fullduplex_format[]; @@ -295,6 +305,12 @@ extern const char report_sumcnt_udp_fullduplex_format[]; +extern const char report_client_bb_bw_header[]; + +extern const char report_client_bb_bw_format[]; + +extern const char report_client_bb_bw_triptime_format[]; + /* ------------------------------------------------------------------- * Misc reports * ------------------------------------------------------------------- */ @@ -317,6 +333,8 @@ extern const char report_mss[]; +extern const char report_default_mss[]; + extern const char report_datagrams[]; extern const char report_sumcnt_datagrams[]; diff -Nru iperf-2.1.5+dfsg1/include/packet_ring.h iperf-2.1.7+dfsg1/include/packet_ring.h --- iperf-2.1.5+dfsg1/include/packet_ring.h 2021-12-05 20:49:56.000000000 +0000 +++ iperf-2.1.7+dfsg1/include/packet_ring.h 2022-04-11 18:13:03.000000000 +0000 @@ -49,6 +49,7 @@ #define PACKETRINGC_H #include "Condition.h" +#include "gettcpinfo.h" #ifdef __cplusplus extern "C" { @@ -56,14 +57,6 @@ #define ACKRING_DEFAULTSIZE 100 -struct reportstruct_tcpstats { - bool isValid; - int cwnd; - int rtt; - int rttvar; - intmax_t retry_tot; -}; - struct ReportStruct { intmax_t packetID; intmax_t packetLen; @@ -84,9 +77,10 @@ intmax_t remaining; int transit_ready; int writecnt; - struct reportstruct_tcpstats tcpstats; - double select_delay; - long drain_time; + long write_time; + struct timeval sentTimeRX; + struct timeval sentTimeTX; + struct iperf_tcpstats tcpstats; }; struct PacketRing { diff -Nru iperf-2.1.5+dfsg1/include/payloads.h iperf-2.1.7+dfsg1/include/payloads.h --- iperf-2.1.5+dfsg1/include/payloads.h 2021-12-05 20:49:56.000000000 +0000 +++ iperf-2.1.7+dfsg1/include/payloads.h 2022-04-11 18:13:03.000000000 +0000 @@ -87,6 +87,11 @@ #define RUN_NOW 0x00000001 #define HEADER16_SMALL_TRIPTIMES 0x0002 // use is 16 bits and not 32 bits +// Bounceback flag +#define HEADER_BBQUICKACK 0x8000 +#define HEADER_BBCLOCKSYNCED 0x4000 // used in the bb header only +#define HEADER_BBTOS 0x2000 + // newer flags available per HEADER_EXTEND // Below flags are used to pass test settings in *every* UDP packet // and not just during the header exchange @@ -105,11 +110,11 @@ #define HEADER_EPOCH_START 0x1000 #define HEADER_PERIODICBURST 0x2000 #define HEADER_WRITEPREFETCH 0x4000 +#define HEADER_TCPQUICKACK 0x8000 // later features #define HDRXACKMAX 2500000 // default 2.5 seconds, units microseconds -#define HDRXACKMIN 10000 // default 10 ms, units microseconds - +#define HDRXACKMIN 10000 // default 10 ms, units microsecond /* * Structures used for test messages which * are exchanged between the client and the Server/Listener @@ -195,12 +200,9 @@ // o) burst id is a running seq no // o) send is the write timestamp // o) bb_r2w_hold is an optional delay value between the read and bb write, typically expected as zero -// o) drain is the time from write to os indicating remote received payload (TCP only) -// o) drain times are deltas and not timestamps, computed by each end, units is microseconds // o) triptimes will support OWD measurements in each direction (useful for asymmetry testing) // o) min payload // - seven 32b or 28 bytes with round trip only, -// - nine 32b or 36 bytes when including drain times, // - eleven 32b or 44 bytes when including trip-times support // o) no need for a bb read timestamp to be passed in the payload // o) OWD calculations require e2e clock sync and --trip-times cli option @@ -213,19 +215,18 @@ uint32_t sec; uint32_t usec; }; -union bb_r2w_info { - struct bb_ts bb_r2w_hold; - struct bb_ts bb_send; -}; -struct bounce_back_datagram_hdr { +struct bounceback_hdr { uint32_t flags; - uint32_t burst_size; - uint32_t burst_id; - struct bb_ts send_ts; - union bb_r2w_info bb_r2w; // up to here is mandatory - uint32_t drain; //units of usecs - uint32_t bb_drain; - struct bb_ts bb_read; + uint32_t bbsize; + uint32_t bbid; + uint16_t bbflags; + uint16_t tos; + struct bb_ts bbclientTx_ts; + struct bb_ts bbserverRx_ts; + struct bb_ts bbserverTx_ts; + uint32_t bbhold; // up to here is mandatory + uint32_t bbrtt; + struct bb_ts bbread_ts; }; struct client_hdrext_isoch_settings { @@ -609,7 +610,7 @@ #define SIZEOF_UDPHDRMSG_EXT (sizeof(struct client_udp_testhdr)) #define SIZEOF_TCPHDRMSG_V1 (sizeof(struct client_hdr_v1)) #define SIZEOF_TCPHDRMSG_EXT (sizeof(struct client_tcp_testhdr)) -#define MINMBUFALLOCSIZE (int) (sizeof(struct client_tcp_testhdr)) +#define MINMBUFALLOCSIZE (int) (sizeof(struct client_tcp_testhdr)) + TAPBYTESSLOP #define MINTRIPTIMEPLAYOAD (int) (sizeof(struct client_udp_testhdr) - sizeof(struct client_hdrext_isoch_settings)) #ifdef __cplusplus } /* end extern "C" */ diff -Nru iperf-2.1.5+dfsg1/include/PerfSocket.hpp iperf-2.1.7+dfsg1/include/PerfSocket.hpp --- iperf-2.1.5+dfsg1/include/PerfSocket.hpp 2021-12-05 20:49:56.000000000 +0000 +++ iperf-2.1.7+dfsg1/include/PerfSocket.hpp 2022-04-11 18:13:03.000000000 +0000 @@ -58,11 +58,23 @@ #include "Mutex.h" #include "Settings.hpp" +#ifdef __cplusplus +extern "C" { +#endif // int timer units is micorseconds void SetSocketOptions(struct thread_Settings *inSettings); void SetSocketOptionsSendTimeout(struct thread_Settings *mSettings, int timer); void SetSocketOptionsReceiveTimeout(struct thread_Settings *mSettings, int timer); void SetSocketOptionsIPTos (struct thread_Settings *mSettings, int tos); +void setsock_tcp_mss(int inSock, int inMSS); +int getsock_tcp_mss(int inSock); +#ifdef DEFAULT_PAYLOAD_LEN_PER_MTU_DISCOVERY +void checksock_max_udp_payload (struct thread_Settings *inSettings); +#endif + +#ifdef __cplusplus +} /* end extern "C" */ +#endif #endif // PERFSOCKET_H diff -Nru iperf-2.1.5+dfsg1/include/Reporter.h iperf-2.1.7+dfsg1/include/Reporter.h --- iperf-2.1.5+dfsg1/include/Reporter.h 2021-12-05 20:49:56.000000000 +0000 +++ iperf-2.1.7+dfsg1/include/Reporter.h 2022-04-11 18:13:03.000000000 +0000 @@ -57,6 +57,7 @@ #include "Mutex.h" #include "histogram.h" #include "packet_ring.h" +#include "gettcpinfo.h" // forward declarations found in Settings.hpp struct thread_Settings; @@ -73,8 +74,8 @@ // If the minimum latency exceeds the boundaries below // assume the clocks are not synched and suppress the // latency output. Units are seconds -#define UNREALISTIC_LATENCYMINMIN -0.01 -#define UNREALISTIC_LATENCYMINMAX 60 +#define UNREALISTIC_LATENCYMINMIN -0.01*1e6 +#define UNREALISTIC_LATENCYMINMAX 60*1e6 #ifdef __cplusplus extern "C" { @@ -90,23 +91,6 @@ * Used for end/end latency measurements * */ -struct TransitStats { - double maxTransit; - double minTransit; - double sumTransit; - double lastTransit; - double meanTransit; - double m2Transit; - double vdTransit; - int cntTransit; - double totmaxTransit; - double totminTransit; - double totsumTransit; - int totcntTransit; - double totmeanTransit; - double totm2Transit; - double totvdTransit; -}; struct MeanMinMaxStats { double max; @@ -134,20 +118,9 @@ int WriteErr; int totWriteCnt; int totWriteErr; -#if (HAVE_TCP_STATS) - int TCPretry; - int totTCPretry; - int cwnd; - int rtt; - int rttvar; -#endif + struct iperf_tcpstats tcpstats; }; -struct tcp_init_conditions { - int cwnd; - int rtt; - double connecttime; -}; /* * This struct contains all important information from the sending or * recieving thread. @@ -174,7 +147,7 @@ intmax_t tot_lengtherr; }; -struct DrainStats { +struct RunningMMMStats { struct MeanMinMaxStats current; struct MeanMinMaxStats total; }; @@ -255,6 +228,8 @@ double rtt_weight; double ListenerTimeout; double FPS; + int bbsize; + int bbhold; #if WIN32 SOCKET socket; #else @@ -273,8 +248,7 @@ int winsize; char peerversion[PEERVERBUFSIZE]; struct MeanMinMaxStats connect_times; - int MSS; - struct tcp_init_conditions init_cond; + struct iperf_tcpstats tcpinitstats; }; struct ShiftIntCounter { @@ -318,6 +292,7 @@ iperf_sockaddr local; Socklen_t size_local; int pid; + int sockmaxseg; struct IsochStats isochstats; void (*output_handler) (struct ReportSettings *settings); }; @@ -327,7 +302,7 @@ INTERVAL = 0, FINALPARTIAL, TOTAL, - FRAME + INTERVALPARTIAL }; struct ReportTimeStamps { @@ -342,7 +317,7 @@ struct timeval nextTime; struct timeval intervalTime; struct timeval IPGstart; - struct timeval nextTCPStampleTime; + struct timeval nextTCPSampleTime; }; struct TransferInfo { @@ -359,15 +334,19 @@ intmax_t cntIPG; intmax_t PacketID; double jitter; - double tripTime; double IPGsum; struct ShiftCounters total; // Shift counters used to calculate interval reports and hold totals union SendReadStats sock_callstats; struct IsochStats isochstats; struct histogram *latency_histogram; - struct TransitStats transit; + struct RunningMMMStats transit; struct histogram *framelatency_histogram; - struct TransitStats frame; + struct RunningMMMStats frame; // isochronous frame or msg burst + struct histogram *bbrtt_histogram; + struct RunningMMMStats bbrtt; + struct RunningMMMStats bbowdto; + struct RunningMMMStats bbowdfro; + struct RunningMMMStats bbasym; struct L2Stats l2counts; // Packet and frame state info uint32_t matchframeID; @@ -376,10 +355,8 @@ bool final; bool burstid_transition; bool isEnableTcpInfo; -#if HAVE_DECL_TCP_NOTSENT_LOWAT - struct DrainStats drain_mmm; - struct histogram *drain_histogram; -#endif + struct RunningMMMStats write_mmm; + struct histogram *write_histogram; }; struct SumReport { @@ -430,7 +407,7 @@ void SetSumHandlers (struct thread_Settings *inSettings, struct SumReport* sumreport); struct SumReport* InitSumReport(struct thread_Settings *inSettings, int inID, int fullduplex); struct ReportHeader* InitIndividualReport(struct thread_Settings *inSettings); -struct ReportHeader* InitConnectionReport(struct thread_Settings *inSettings, struct tcp_init_conditions *init_cond); +struct ReportHeader* InitConnectionReport(struct thread_Settings *inSettings); struct ConnectionInfo* InitConnectOnlyReport(struct thread_Settings *thread); struct ReportHeader *InitSettingsReport(struct thread_Settings *inSettings); struct ReportHeader* InitServerRelayUDPReport(struct thread_Settings *inSettings, struct server_hdr *server); @@ -461,8 +438,8 @@ void reporter_handle_packet_server_udp(struct ReporterData *data, struct ReportStruct *packet); void reporter_handle_packet_server_tcp(struct ReporterData *data, struct ReportStruct *packet); void reporter_handle_packet_client(struct ReporterData *data, struct ReportStruct *packet); -void reporter_handle_packet_pps(struct ReporterData *data, struct ReportStruct *packet); -void reporter_handle_packet_isochronous(struct ReporterData *data, struct ReportStruct *packet); +void reporter_handle_packet_bb_client(struct ReporterData *data, struct ReportStruct *packet); +void reporter_handle_packet_bb_server(struct ReporterData *data, struct ReportStruct *packet); // Reporter's conditional prints, right now have time and frame based sampling, possibly add packet based int reporter_condprint_time_interval_report(struct ReporterData *data, struct ReportStruct *packet); @@ -481,8 +458,10 @@ //void reporter_transfer_protocol_reports(struct ReporterData *stats, struct ReportStruct *packet); //void reporter_transfer_protocol_multireports(struct ReporterData *stats, struct ReportStruct *packet); void reporter_transfer_protocol_client_tcp(struct ReporterData *data, int final); +void reporter_transfer_protocol_client_bb_tcp(struct ReporterData *data, int final); void reporter_transfer_protocol_client_udp(struct ReporterData *data, int final); void reporter_transfer_protocol_server_tcp(struct ReporterData *data, int final); +void reporter_transfer_protocol_server_bb_tcp(struct ReporterData *data, int final); void reporter_transfer_protocol_server_udp(struct ReporterData *data, int final); // Reporter's sum output routines (per -P > 1) @@ -509,15 +488,15 @@ // TCP client void tcp_output_write(struct TransferInfo *stats); +void tcp_output_burst_write(struct TransferInfo *stats); void tcp_output_sum_write(struct TransferInfo *stats); void tcp_output_sumcnt_write(struct TransferInfo *stats); void tcp_output_write_enhanced (struct TransferInfo *stats); void tcp_output_write_enhanced_isoch (struct TransferInfo *stats); void tcp_output_sum_write_enhanced (struct TransferInfo *stats); void tcp_output_sumcnt_write_enhanced (struct TransferInfo *stats); -#if (HAVE_DECL_TCP_NOTSENT_LOWAT) -void tcp_output_write_enhanced_drain (struct TransferInfo *stats); -#endif +void tcp_output_write_enhanced_write (struct TransferInfo *stats); +void tcp_output_write_bb(struct TransferInfo *stats); // TCP fullduplex void tcp_output_fullduplex(struct TransferInfo *stats); void tcp_output_fullduplex_enhanced(struct TransferInfo *stats); @@ -525,7 +504,6 @@ // UDP server void udp_output_read(struct TransferInfo *stats); -void udp_output_read_enhanced(struct TransferInfo *stats); void udp_output_read_enhanced_triptime(struct TransferInfo *stats); void udp_output_read_enhanced_triptime_isoch(struct TransferInfo *stats); void udp_output_sum_read(struct TransferInfo *stats); diff -Nru iperf-2.1.5+dfsg1/include/Server.hpp iperf-2.1.7+dfsg1/include/Server.hpp --- iperf-2.1.5+dfsg1/include/Server.hpp 2021-12-05 20:49:56.000000000 +0000 +++ iperf-2.1.7+dfsg1/include/Server.hpp 2022-04-11 18:13:03.000000000 +0000 @@ -71,7 +71,7 @@ // accepts connection and receives data void RunUDP(void); void RunTCP(void); - void RunTcpBounceBack(void); + void RunBounceBackTCP(void); static void Sig_Int(int inSigno); private: @@ -85,8 +85,9 @@ bool InitTrafficLoop(void); inline void SetFullDuplexReportStartTime(void); inline void SetReportStartTime(); + bool ReadBBWithRXTimestamp (); int ReadWithRxTimestamp(void); - bool ReadPacketID(void); + bool ReadPacketID(int); void L2_processing(void); int L2_quintuple_filter(void); void udp_isoch_processing(int); diff -Nru iperf-2.1.5+dfsg1/include/Settings.hpp iperf-2.1.7+dfsg1/include/Settings.hpp --- iperf-2.1.5+dfsg1/include/Settings.hpp 2021-12-05 20:49:56.000000000 +0000 +++ iperf-2.1.7+dfsg1/include/Settings.hpp 2022-04-11 18:13:03.000000000 +0000 @@ -285,7 +285,9 @@ double mListenerTimeout; int tuntapdev; int firstreadbytes; - int mBounceBack; + int mBounceBackBytes; + uint32_t mBounceBackHold; // units of usecs + struct iperf_tcpstats tcpinitstats; #if HAVE_DECL_TCP_WINDOW_CLAMP int mClampSize; #endif @@ -351,7 +353,7 @@ #define FLAG_FQPACING 0x00001000 #define FLAG_TRIPTIME 0x00002000 #define FLAG_TXHOLDBACK 0x00004000 -#define FLAG_TCPWRITETIME 0x00008000 +#define FLAG_UNUSED 0x00008000 #define FLAG_MODEINFINITE 0x00010000 #define FLAG_CONNECTONLY 0x00020000 #define FLAG_SERVERREVERSE 0x00040000 @@ -381,10 +383,12 @@ #define FLAG_TAPDEV 0x00000040 #define FLAG_HIDEIPS 0x00000080 #define FLAG_BOUNCEBACK 0x00000100 -#define FLAG_TCPDRAIN 0x00000200 +#define FLAG_TCPWRITETIMES 0x00000200 #define FLAG_INCRSRCPORT 0x00000400 #define FLAG_OVERRIDETOS 0x00000800 -#define FLAG_DOMAINV4 0x00001000 +#define FLAG_TCPQUICKACK 0x00001000 +#define FLAG_CONGEST 0x00002000 +#define FLAG_DOMAINV4 0x00004000 #define isBuflenSet(settings) ((settings->flags & FLAG_BUFLENSET) != 0) #define isCompat(settings) ((settings->flags & FLAG_COMPAT) != 0) @@ -456,8 +460,10 @@ #define isTunDev(settings) ((settings->flags_extend2 & FLAG_TUNDEV) != 0) #define isHideIPs(settings) ((settings->flags_extend2 & FLAG_HIDEIPS) != 0) #define isBounceBack(settings) ((settings->flags_extend2 & FLAG_BOUNCEBACK) != 0) -#define isTcpDrain(settings) ((settings->flags_extend2 & FLAG_TCPDRAIN) != 0) +#define isTcpWriteTimes(settings) ((settings->flags_extend2 & FLAG_TCPWRITETIMES) != 0) #define isOverrideTOS(settings) ((settings->flags_extend2 & FLAG_OVERRIDETOS) != 0) +#define isTcpQuickAck(settings) ((settings->flags_extend2 & FLAG_TCPQUICKACK) != 0) +#define isCongest(settings) ((settings->flags_extend2 & FLAG_CONGEST) != 0) #define setBuflenSet(settings) settings->flags |= FLAG_BUFLENSET #define setCompat(settings) settings->flags |= FLAG_COMPAT @@ -526,8 +532,10 @@ #define setTunDev(settings) settings->flags_extend2 |= FLAG_TUNDEV #define setHideIPs(settings) settings->flags_extend2 |= FLAG_HIDEIPS #define setBounceBack(settings) settings->flags_extend2 |= FLAG_BOUNCEBACK -#define setTcpDrain(settings) settings->flags_extend2 |= FLAG_TCPDRAIN +#define setTcpWriteTimes(settings) settings->flags_extend2 |= FLAG_TCPWRITETIMES #define setOverrideTOS(settings) settings->flags_extend2 |= FLAG_OVERRIDETOS +#define setTcpQuickAck(settings) settings->flags_extend2 |= FLAG_TCPQUICKACK +#define setCongest(settings) settings->flags_extend2 |= FLAG_CONGEST #define unsetBuflenSet(settings) settings->flags &= ~FLAG_BUFLENSET #define unsetCompat(settings) settings->flags &= ~FLAG_COMPAT @@ -595,8 +603,10 @@ #define unsetTunDev(settings) settings->flags_extend2 &= ~FLAG_TUNDEV #define unsetHideIPs(settings) settings->flags_extend2 &= ~FLAG_HIDEIPS #define unsetBounceBack(settings) settings->flags_extend2 &= ~FLAG_BOUNCEBACK -#define unsetTcpDrain(settings) settings->flags_extend2 &= ~FLAG_TCPDRAIN +#define unsetTcpWriteTimes(settings) settings->flags_extend2 &= ~FLAG_TCPWRITETIMES #define unsetOverrideTOS(settings) settings->flags_extend2 &= ~FLAG_OVERRIDETOS +#define unsetTcpQuickAck(settings) settings->flags_extend2 &= ~FLAG_TCPQUICKACK +#define unsetCongest(settings) settings->flags_extend2 &= ~FLAG_CONGEST // set to defaults void Settings_Initialize(struct thread_Settings* main); diff -Nru iperf-2.1.5+dfsg1/include/util.h iperf-2.1.7+dfsg1/include/util.h --- iperf-2.1.5+dfsg1/include/util.h 2021-12-05 20:49:56.000000000 +0000 +++ iperf-2.1.7+dfsg1/include/util.h 2022-04-11 18:13:03.000000000 +0000 @@ -70,8 +70,6 @@ int setsock_tcp_windowsize(int inSock, int inTCPWin, int inSend); int getsock_tcp_windowsize(int inSock, int inSend); -void setsock_tcp_mss(int inSock, int inMSS); -int getsock_tcp_mss(int inSock); bool setsock_blocking(int fd, bool blocking); #if HAVE_DECL_TCP_WINDOW_CLAMP int getsock_tcp_windowclamp(int inSock); @@ -85,6 +83,7 @@ int writen(int inSock, const void *inBuf, int inLen, int *count); void disarm_itimer(void); +int set_itimer(int); /* ------------------------------------------------------------------- * signal handlers * signal.c @@ -193,8 +192,11 @@ #define TimeZero(timeval) ((timeval.tv_sec == 0) && (timeval.tv_usec == 0)) -#define TimeDifference(left, right) (left.tv_sec - right.tv_sec) + \ - (left.tv_usec - right.tv_usec) / ((double) rMillion) +#define TimeDifference(left, right) ((left.tv_sec - right.tv_sec) + \ + (left.tv_usec - right.tv_usec) / ((double) rMillion)) + +#define TimeDifferenceUsec(left, right) ((1e6 * (left.tv_sec - right.tv_sec)) + \ + (double) (left.tv_usec - right.tv_usec)) #define TimeDouble(timeval) (timeval.tv_sec + timeval.tv_usec / ((double) rMillion)) diff -Nru iperf-2.1.5+dfsg1/include/version.h iperf-2.1.7+dfsg1/include/version.h --- iperf-2.1.5+dfsg1/include/version.h 2021-12-05 20:50:04.000000000 +0000 +++ iperf-2.1.7+dfsg1/include/version.h 2022-04-11 18:13:42.000000000 +0000 @@ -1,8 +1,8 @@ #include "config.h" #define IPERF_VERSION VERSION -#define IPERF_VERSION_DATE "3 December 2021" +#define IPERF_VERSION_DATE "11 April 2022" #define IPERF_VERSION_MAJORHEX 0x00020001 -#define IPERF_VERSION_MINORHEX 0x00050003 +#define IPERF_VERSION_MINORHEX 0x00070003 /* * case 0: diff -Nru iperf-2.1.5+dfsg1/man/iperf.1 iperf-2.1.7+dfsg1/man/iperf.1 --- iperf-2.1.5+dfsg1/man/iperf.1 2021-12-05 20:50:04.000000000 +0000 +++ iperf-2.1.7+dfsg1/man/iperf.1 2022-04-11 18:13:03.000000000 +0000 @@ -1,4 +1,4 @@ -.TH IPERF 1 "October 2021" NLANR/DAST "User Manuals" +.TH IPERF 1 "January 2022" NLANR/DAST "User Manuals" .SH NAME iperf \- perform network traffic tests using network sockets. Metrics include throughput and latency. .SH SYNOPSIS @@ -54,7 +54,7 @@ perform layer 2 length checks on received UDP packets (requires systems that support packet sockets, e.g. Linux) .TP .BR -m ", " --print_mss " " -print TCP maximum segment size (MTU - TCP/IP header) +print TCP maximum segment size .TP .BR " --NUM_REPORT_STRUCTS " \fI\fR Override the default shared memory size between the traffic thread(s) and reporter thread in order to mitigate mutex lock contentions. The default value of 5000 should be sufficient for 1Gb/s networks. Increase this upon seeing the Warning message of reporter thread too slow. If the Warning message isn't seen, then increasing this won't have any significant effect (other than to use some additional memory.) @@ -93,7 +93,7 @@ for use with older versions does not sent extra msgs .TP .BR -M ", " --mss " \fIn\fR" -set TCP maximum segment size (MTU - 40 bytes) +set TCP maximum segment size using TCP_MAXSEG .TP .BR -N ", " --nodelay " " set TCP no delay, disabling Nagle's Algorithm @@ -139,7 +139,7 @@ time in seconds to listen for new traffic connections and/or receive traffic (defaults to infinite) .TP .BR " --tos-override " \fIn\fR -set the socket's IP_TOS (byte) field for reverse or full duplex traffic. Supported in versions 2.1.5 or greater. Previous versions won't set IP_TOSq on reverse traffic. +set the socket's IP_TOS (byte) field for reverse or full duplex traffic. Supported in versions 2.1.5 or greater. Previous versions won't set IP_TOS on reverse traffic. .TP .BR -B ", " --bind " \fIip\fR | \fIip\fR%\fIdevice\fR" bind src ip addr and optional src device for receiving @@ -168,14 +168,29 @@ set target bandwidth to \fIn\fR bits/sec (default 1 Mbit/sec) or \fIn\fR packets per sec. This may be used with TCP or UDP. Optionally, for variable loads, use format of mean,standard deviation .TP -.BR -c ", " --client " \fI\fIhost\fR | \fIhost\fR%\fIdevice\fR" -run in client mode, connecting to \fIhost\fR where the optional %dev will SO_BINDTODEVICE that output interface (requires root and see NOTES) +.BR " --bounceback " +run a tcp bounceback test (set size with -l or --len, defaults to 100 bytes) +.TP +.BR " --bounceback-congest " +request a concurrent full-duplex TCP stream +.TP +.BR " --bounceback-hold " \fIn\fR +request the server to insert a delay of n milliseconds between its read and write (default is no delay) +.TP +.BR " --bounceback-period " \fIn\fR +request the client schedule a send every n milliseconds (w/o option bouncebacks are immediately scheduled. With command option and no optional value given, the interval will be one second.) +.TP +.BR " --bounceback-no-quickack " +request the server not set the TCP_QUICKACK socket option (disabling TCP ACK delays) during a bounceback test (see NOTES) .TP .BR " --burst-period " \fIn\fR Set the burst period in seconds. Defaults to one second. (Note: assumed use case is low duty cycle traffic bursts) .TP .BR " --burst-size " \fIn\fR Set the burst size in bytes. Defaults to 1M if no value is given. +.BR -c ", " --client " \fI\fIhost\fR | \fIhost\fR%\fIdevice\fR" +run in client mode, connecting to \fIhost\fR where the optional %dev will SO_BINDTODEVICE that output interface (requires root and see NOTES) +.TP .TP .BR " --connect-only[=" \fIn\fR "]" only perform a TCP connect (or 3WHS) without any data transfer, useful to measure TCP connect() times. Optional value of n is the total number of connects to do (zero is run forever.) Note that -i will rate limit the connects where -P will create bursts and -t will end the client and hence end its connect attempts. @@ -239,12 +254,15 @@ Do a bidirectional test individually - client-to-server, followed by a reversed test, server-to-client .TP -.BR " --tcp-drain " -This is an experimental feature to measure the sending (client) host's sojourn times. Measure delay after completion of writing a burst (set via -l or --burst-size) and when TCP_NOTSENT_LOWAT set to a small value triggers the select() indicating all bytes per the burst are inflight. Output is a D8 histogram on the client side. +.BR " --tcp-quickack " +Set TCP_QUICKACK on the socket .TP .BR " --tcp-write-prefetch " \fIn\fR[kmKM] Set TCP_NOTSENT_LOWAT on the socket and use event based writes per select() on the socket. .TP +.BR " --tcp-write-times " +Measure the socket write times +.TP .BR -t ", " --time " \fIn\fR" | "\fI0\fR" time in seconds to transmit traffic, use zero for infinite (default is 10 secs) .TP @@ -783,6 +801,20 @@ since there is no flow control with UDP. There is no option to directly rate limit the writes with TCP testing when using --reverse. .P +.B Bounceback +The bounceback test allows one to measure network responsiveness (which, in this test, is an inverse of latency.) +Latency is merely delay in units of time. Latency metrics require one to know the delay of what's being measured. +For bounceback it's a client write to a server read followed by a server write and then the client read. The original +write is bounce backed. Iperf 2 sets up the socket with TCP_NODELAY and possibly TCP_QUICKACK (unless disabled). +The client sends a small write (which defaults to 100 bytes unless -l is set) and issues a read waiting for the "bounceback" +from the server. The server waits for a read and then optionally delays before sending the payload back. +This repeats until the traffic ends. +.P +The TCP_QUICKACK socket option will be enabled during bounceback tests when the bounceback-hold +is set to a non-zero value. The socket option is applied after every read() on the server +and before the hold delay call. It's also applied on the client. Use --bounceback-no-quickack +to disable TCP ack delays per the socket. +.P .B TCP Connect times: The TCP connect time (or three way handshake) can be seen on the iperf client when the -e (--enhanced) option is set. Look for the @@ -819,6 +851,9 @@ .B Multicast: Iperf 2 supports multicast with a couple of caveats. First, multicast streams cannot take advantage of the -P option. The server will serialize multicast streams. Also, it's highly encouraged to use a -t on a server that will be used for multicast clients. That is because the single end of traffic packet sent from client to server may get lost and there are no redundant end of traffic packets. Setting -t on the server will kill the server thread in the event this packet is indeed lost. .P +.B TCP_QUICACK: +The TCP_QUICKACK socket applied after every read() on the server +.P .B Fast Sampling: Use .B ./configure --enable-fastsampling diff -Nru iperf-2.1.5+dfsg1/src/Client.cpp iperf-2.1.7+dfsg1/src/Client.cpp --- iperf-2.1.5+dfsg1/src/Client.cpp 2021-12-05 20:49:56.000000000 +0000 +++ iperf-2.1.7+dfsg1/src/Client.cpp 2022-04-11 18:13:03.000000000 +0000 @@ -97,7 +97,6 @@ unsetPeerVerDetect(mSettings); } - pattern(mSettings->mBuf, mSettings->mBufLen); if (isFileInput(mSettings)) { if (!isSTDIN(mSettings)) Extractor_Initialize(mSettings->mFileName, mSettings->mBufLen, mSettings); @@ -112,7 +111,7 @@ FAIL_errno(!(mSettings->mFPS > 0.0), "Invalid value for frames per second in the isochronous settings\n", mSettings); } peerclose = false; - isburst = (isIsochronous(mSettings) || isPeriodicBurst(mSettings) || ((isTripTime(mSettings) || isTcpDrain(mSettings)) && !isUDP(mSettings))); + isburst = (isIsochronous(mSettings) || isPeriodicBurst(mSettings) || (isTripTime(mSettings) && !isUDP(mSettings))); } // end Client /* ------------------------------------------------------------------- @@ -159,9 +158,16 @@ WARN_errno(rc == SOCKET_ERROR, "bind"); } + if (!isUDP(mSettings) && isReport(mSettings) && isSettingsReport(mSettings)) { + struct ReportHeader *tmp = InitSettingsReport(mSettings); + assert(tmp!=NULL); + PostReport(tmp); + setNoSettReport(mSettings); + } + // connect socket connected = false; - my_init_cond.connecttime = -1; + mSettings->tcpinitstats.connecttime = -1; if (!isUDP(mSettings)) { int trycnt = mSettings->mConnectRetries + 1; while (trycnt > 0) { @@ -180,8 +186,7 @@ } } else { connect_done.setnow(); - my_init_cond.connecttime = 1e3 * connect_done.subSec(connect_start); - mSettings->connecttime = my_init_cond.connecttime; + mSettings->tcpinitstats.connecttime = 1e3 * connect_done.subSec(connect_start); connected = true; break; } @@ -189,20 +194,16 @@ } else { rc = connect(mySocket, reinterpret_cast(&mSettings->peer), SockAddr_get_sizeof_sockaddr(&mSettings->peer)); - my_init_cond.connecttime = 0.0; // UDP doesn't have a 3WHS + mSettings->tcpinitstats.connecttime = 0.0; // UDP doesn't have a 3WHS WARN_errno((rc == SOCKET_ERROR), "udp connect"); if (rc != SOCKET_ERROR) connected = true; } - my_init_cond.rtt = -1; - my_init_cond.cwnd = -1; if (connected) { #if HAVE_TCP_STATS assert(reportstruct); - if (!isUDP(mSettings) && connected) { - gettcpinfo(mySocket, reportstruct); - my_init_cond.rtt = reportstruct->tcpstats.rtt; - my_init_cond.cwnd = reportstruct->tcpstats.cwnd; + if (!isUDP(mSettings)) { + gettcpinfo(mySocket, &mSettings->tcpinitstats); } #endif // Set the send timeout for the very first write which has the test exchange @@ -211,9 +212,20 @@ getsockname(mySocket, reinterpret_cast(&mSettings->local), &mSettings->size_local); getpeername(mySocket, reinterpret_cast(&mSettings->peer), &mSettings->size_peer); SockAddr_Ifrname(mSettings); +#ifdef DEFAULT_PAYLOAD_LEN_PER_MTU_DISCOVERY + if (isUDP(mSettings) && !isBuflenSet(mSettings)) { + checksock_max_udp_payload(mSettings); + } +#endif if (isUDP(mSettings) && !isIsochronous(mSettings) && !isIPG(mSettings)) { mSettings->mBurstIPG = get_delay_target() / 1e3; // this is being set for the settings report only } + if (isReport(mSettings) && isSettingsReport(mSettings)) { + struct ReportHeader *tmp = InitSettingsReport(mSettings); + assert(tmp!=NULL); + PostReport(tmp); + setNoSettReport(mSettings); + } } else { if (mySocket != INVALID_SOCKET) { int rc = close(mySocket); @@ -221,23 +233,17 @@ mySocket = INVALID_SOCKET; } } - if (isReport(mSettings) && isSettingsReport(mSettings)) { - struct ReportHeader *tmp = InitSettingsReport(mSettings); - assert(tmp!=NULL); - PostReport(tmp); - setNoSettReport(mSettings); - } // Post the connect report unless peer version exchange is set if (isConnectionReport(mSettings) && !isSumOnly(mSettings)) { if (connected) { - struct ReportHeader *reporthdr = InitConnectionReport(mSettings, &my_init_cond); + struct ReportHeader *reporthdr = InitConnectionReport(mSettings); struct ConnectionInfo *cr = static_cast(reporthdr->this_report); cr->connect_timestamp.tv_sec = connect_start.getSecs(); cr->connect_timestamp.tv_usec = connect_start.getUsecs(); assert(reporthdr); PostReport(reporthdr); } else { - PostReport(InitConnectionReport(mSettings, &my_init_cond)); + PostReport(InitConnectionReport(mSettings)); } } return connected; @@ -282,7 +288,7 @@ // check for an epoch based start time reportstruct->packetLen = 0; if (!isServerReverse(mSettings)) { - if (!isCompat(mSettings)) { + if (!isCompat(mSettings) && !isBounceBack(mSettings)) { reportstruct->packetLen = SendFirstPayload(); // Reverse UDP tests need to retry "first sends" a few times // before going to server or read mode @@ -331,11 +337,11 @@ // Near congestion and peridiodic need sampling on every report packet if (isNearCongest(mSettings) || isPeriodicBurst(mSettings)) { myReport->info.isEnableTcpInfo = true; - myReport->info.ts.nextTCPStampleTime.tv_sec = 0; - myReport->info.ts.nextTCPStampleTime.tv_usec = 0; - } else if (isEnhanced(mSettings)) { + myReport->info.ts.nextTCPSampleTime.tv_sec = 0; + myReport->info.ts.nextTCPSampleTime.tv_usec = 0; + } else if (isEnhanced(mSettings) || isBounceBack(mSettings)) { myReport->info.isEnableTcpInfo = true; - myReport->info.ts.nextTCPStampleTime = myReport->info.ts.nextTime; + myReport->info.ts.nextTCPSampleTime = myReport->info.ts.nextTime; } } #endif @@ -480,6 +486,7 @@ if (isModeTime(mSettings)) { mEndTime.setnow(); mEndTime.add(mSettings->mAmount / 100.0); + // now.setnow(); fprintf(stderr, "DEBUG: end time set to %ld.%ld now is %ld.%ld\n", mEndTime.getSecs(), mEndTime.getUsecs(), now.getSecs(), now.getUsecs()); } readAt = mSettings->mBuf; lastPacketTime.set(myReport->info.ts.startTime.tv_sec, myReport->info.ts.startTime.tv_usec); @@ -530,7 +537,9 @@ } } else { // Launch the approprate TCP traffic loop - if (mSettings->mAppRate > 0) { + if (isBounceBack(mSettings)) { + RunBounceBackTCP(); + } else if (mSettings->mAppRate > 0) { RunRateLimitedTCP(); } else if (isNearCongest(mSettings)) { RunNearCongestionTCP(); @@ -556,6 +565,7 @@ now.setnow(); reportstruct->packetTime.tv_sec = now.getSecs(); reportstruct->packetTime.tv_usec = now.getUsecs(); + reportstruct->write_time = 0; while (InProgress()) { reportstruct->writecnt = 0; if (isModeAmount(mSettings)) { @@ -580,6 +590,7 @@ if (isPeriodicBurst(mSettings)) { // low duty cycle traffic needs special event handling now.setnow(); + myReport->info.ts.prevsendTime = reportstruct->packetTime; reportstruct->packetTime.tv_sec = now.getSecs(); reportstruct->packetTime.tv_usec = now.getUsecs(); if (!InProgress()) { @@ -607,10 +618,6 @@ myReport->info.ts.prevsendTime = reportstruct->packetTime; writelen = (mSettings->mBufLen > burst_remaining) ? burst_remaining : mSettings->mBufLen; // perform write, full header must succeed -#if HAVE_DECL_TCP_NOTSENT_LOWAT - if (isTcpDrain(mSettings)) - drain_start.setnow(); -#endif reportstruct->packetLen = writen(mySocket, mSettings->mBuf, writelen, &reportstruct->writecnt); FAIL_errno(reportstruct->packetLen < (intmax_t) sizeof(struct TCP_burst_payload), "burst written", mSettings); } else { @@ -618,6 +625,9 @@ // perform write if (isburst) writelen = (mSettings->mBufLen > burst_remaining) ? burst_remaining : mSettings->mBufLen; + if (isTcpWriteTimes(mSettings)) { + write_start.setnow(); + } #if HAVE_DECL_TCP_NOTSENT_LOWAT if (isWritePrefetch(mSettings)) { AwaitWriteSelectEventTCP(); @@ -628,6 +638,9 @@ reportstruct->writecnt++; reportstruct->packetTime.tv_sec = now.getSecs(); reportstruct->packetTime.tv_usec = now.getUsecs(); + if (isTcpWriteTimes(mSettings)) { + reportstruct->write_time = now.subUsec(write_start); + } reportstruct->sentTime = reportstruct->packetTime; } if (reportstruct->packetLen <= 0) { @@ -654,11 +667,7 @@ reportstruct->transit_ready = 0; } else { reportstruct->transit_ready = 1; -#if HAVE_DECL_TCP_NOTSENT_LOWAT - if (isTcpDrain(mSettings)) { - tcp_drain(); - } -#endif + reportstruct->prevSentTime = myReport->info.ts.prevsendTime; } } } @@ -702,10 +711,6 @@ WriteTcpTxHdr(reportstruct, burst_remaining, burst_id++); reportstruct->sentTime = reportstruct->packetTime; myReport->info.ts.prevsendTime = reportstruct->packetTime; -#if HAVE_DECL_TCP_NOTSENT_LOWAT - if (isTcpDrain(mSettings)) - drain_start.setnow(); -#endif // perform write int writelen = (mSettings->mBufLen > burst_remaining) ? burst_remaining : mSettings->mBufLen; reportstruct->packetLen = write(mySocket, mSettings->mBuf, writelen); @@ -897,9 +902,7 @@ timeout.tv_usec = 0; } - Timestamp t1; if ((rc = select(mySocket + 1, NULL, &writeset, NULL, &timeout)) <= 0) { - reportstruct->select_delay = -1; WARN_errno((rc < 0), "select"); #ifdef HAVE_THREAD_DEBUG if (rc == 0) @@ -907,9 +910,6 @@ #endif return false; } - Timestamp t2; - reportstruct->select_delay = t2.subSec(t1); - // printf("*****t1 = %f\n", t2.subSec(t1)); return true; } @@ -925,6 +925,10 @@ writelen = ((mSettings->mAmount < static_cast(mSettings->mBufLen)) ? mSettings->mAmount : mSettings->mBufLen); } now.setnow(); + reportstruct->write_time = 0; + if (isTcpWriteTimes(mSettings)) { + write_start = now; + } bool rc = AwaitWriteSelectEventTCP(); reportstruct->emptyreport = (rc == false) ? 1 : 0; if (rc) { @@ -941,6 +945,9 @@ } reportstruct->packetLen = 0; reportstruct->emptyreport = 1; + } else if (isTcpWriteTimes(mSettings)) { + Timestamp write_end; + reportstruct->write_time = write_end.subUsec(write_start); } } if (isModeAmount(mSettings) && !reportstruct->emptyreport) { @@ -959,7 +966,82 @@ } #endif void Client::RunBounceBackTCP () { - + int burst_id = 0; + int writelen = mSettings->mBufLen; + memset(mSettings->mBuf, 0x5A, sizeof(struct bounceback_hdr)); + if (mSettings->mInterval && (mSettings->mIntervalMode == kInterval_Time)) { + int sotimer = static_cast(round(mSettings->mInterval / 2.0)); + SetSocketOptionsReceiveTimeout(mSettings, sotimer); + SetSocketOptionsSendTimeout(mSettings, sotimer); + } else if (isModeTime(mSettings)) { + int sotimer = static_cast(round(mSettings->mAmount * 10000) / 2); + SetSocketOptionsReceiveTimeout(mSettings, sotimer); + SetSocketOptionsSendTimeout(mSettings, sotimer); + } + if (isModeTime(mSettings)) { + int end_usecs (mSettings->mAmount * 10000); //amount units is 10 ms + if (int err = set_itimer(end_usecs)) { + FAIL_errno(err != 0, "setitimer", mSettings); + } + } + now.setnow(); + reportstruct->packetTime.tv_sec = now.getSecs(); + reportstruct->packetTime.tv_usec = now.getUsecs(); + while (InProgress()) { + int n; + reportstruct->writecnt = 0; + if (framecounter) { + burst_id = framecounter->wait_tick(); + } else { + burst_id++; + } + now.setnow(); + reportstruct->sentTime.tv_sec = now.getSecs(); + reportstruct->sentTime.tv_usec = now.getUsecs(); + WriteTcpTxBBHdr(reportstruct, burst_id); + myReport->info.ts.prevsendTime = reportstruct->sentTime; + reportstruct->packetLen = writen(mySocket, mSettings->mBuf, writelen, &reportstruct->writecnt); + if (reportstruct->packetLen == writelen) { + reportstruct->emptyreport = 0; + totLen += reportstruct->packetLen; + reportstruct->errwrite=WriteNoErr; +#if HAVE_DECL_TCP_QUICKACK + if (isTcpQuickAck(mSettings)) { + int opt = 1; + Socklen_t len = sizeof(opt); + int rc = setsockopt(mySocket, IPPROTO_TCP, TCP_QUICKACK, + reinterpret_cast(&opt), len); + WARN_errno(rc == SOCKET_ERROR, "setsockopt TCP_QUICKACK"); + } +#endif + if ((n = recvn(mySocket, mSettings->mBuf, mSettings->mBounceBackBytes, 0)) == mSettings->mBounceBackBytes) { + struct bounceback_hdr *bbhdr = reinterpret_cast(mSettings->mBuf); + now.setnow(); + reportstruct->sentTimeRX.tv_sec = ntohl(bbhdr->bbserverRx_ts.sec); + reportstruct->sentTimeRX.tv_usec = ntohl(bbhdr->bbserverRx_ts.usec); + reportstruct->sentTimeTX.tv_sec = ntohl(bbhdr->bbserverTx_ts.sec); + reportstruct->sentTimeTX.tv_usec = ntohl(bbhdr->bbserverTx_ts.usec); + reportstruct->packetTime.tv_sec = now.getSecs(); + reportstruct->packetTime.tv_usec = now.getUsecs(); + reportstruct->packetLen += n; + reportstruct->emptyreport = 0; + myReportPacket(); + } else if (n == 0) { + peerclose = true; + } + } else if ((reportstruct->packetLen < 0 ) && NONFATALTCPWRITERR(errno)) { + reportstruct->packetLen = 0; + reportstruct->emptyreport = 1; + reportstruct->errwrite=WriteErrNoAccount; + myReportPacket(); + } else { + reportstruct->errwrite=WriteErrFatal; + reportstruct->packetLen = -1; + FAIL_errno(1, "tcp bounce-back write", mSettings); + } + } + disarm_itimer(); + FinishTrafficActions(); } /* * UDP send loop @@ -1007,6 +1089,7 @@ // default: break; //} now.setnow(); + reportstruct->writecnt = 1; reportstruct->packetTime.tv_sec = now.getSecs(); reportstruct->packetTime.tv_usec = now.getUsecs(); reportstruct->sentTime = reportstruct->packetTime; @@ -1305,6 +1388,35 @@ // printf("**** Write tcp burst header size= %d id = %d\n", burst_size, burst_id); } +// See payloads.h +void Client::WriteTcpTxBBHdr (struct ReportStruct *reportstruct, int bbid) { + struct bounceback_hdr * mBuf_bb = reinterpret_cast(mSettings->mBuf); + // store packet ID into buffer + uint32_t flags = HEADER_BOUNCEBACK; + uint32_t bbflags = 0x0; + mBuf_bb->flags = htonl(flags); + if (isTripTime(mSettings)) { + bbflags |= HEADER_BBCLOCKSYNCED; + } + if (mSettings->mTOS) { + bbflags |= HEADER_BBTOS; + mBuf_bb->tos = htons((mSettings->mTOS & 0xFF)); + } + if (isTcpQuickAck(mSettings)) { + bbflags |= HEADER_BBQUICKACK; + } + mBuf_bb->bbflags = htons(bbflags); + mBuf_bb->bbsize = htonl(mSettings->mBufLen); + mBuf_bb->bbid = htonl(bbid); + mBuf_bb->bbclientTx_ts.sec = htonl(reportstruct->packetTime.tv_sec); + mBuf_bb->bbclientTx_ts.usec = htonl(reportstruct->packetTime.tv_usec); + mBuf_bb->bbserverRx_ts.sec = -1; + mBuf_bb->bbserverRx_ts.usec = -1; + mBuf_bb->bbserverTx_ts.sec = -1; + mBuf_bb->bbserverTx_ts.usec = -1; + mBuf_bb->bbhold = htonl(mSettings->mBounceBackHold); +} + inline bool Client::InProgress (void) { // Read the next data block from // the file if it's file input @@ -1312,22 +1424,12 @@ Extractor_getNextDataBlock(readAt, mSettings); return Extractor_canRead(mSettings) != 0; } + // fprintf(stderr, "DEBUG: SI=%d PC=%d T=%d A=%d\n", sInterupted, peerclose, (isModeTime(mSettings) && mEndTime.before(reportstruct->packetTime)), (isModeAmount(mSettings) && (mSettings->mAmount <= 0))); return !(sInterupted || peerclose || \ (isModeTime(mSettings) && mEndTime.before(reportstruct->packetTime)) || (isModeAmount(mSettings) && (mSettings->mAmount <= 0))); } -inline void Client::tcp_drain (void) { -#if HAVE_DECL_TCP_NOTSENT_LOWAT - AwaitWriteSelectEventTCP(); - drain_end.setnow(); - reportstruct->drain_time = drain_end.subUsec(drain_start); -#ifdef HAVE_THREAD_DEBUG - thread_debug("Drain time = %f", reportstruct->drain_time); -#endif -#endif -} - inline void Client::tcp_shutdown (void) { if ((mySocket != INVALID_SOCKET) && isConnected()) { int rc = shutdown(mySocket, SHUT_WR); @@ -1526,6 +1628,7 @@ reportstruct->packetTime.tv_sec = now.getSecs(); reportstruct->packetTime.tv_usec = now.getUsecs(); } + pattern(mSettings->mBuf, mSettings->mBufLen); if (isTxStartTime(mSettings)) { pktlen = Settings_GenerateClientHdr(mSettings, (void *) mSettings->mBuf, mSettings->txstart_epoch); } else { diff -Nru iperf-2.1.5+dfsg1/src/histogram.c iperf-2.1.7+dfsg1/src/histogram.c --- iperf-2.1.5+dfsg1/src/histogram.c 2021-12-05 20:49:56.000000000 +0000 +++ iperf-2.1.7+dfsg1/src/histogram.c 2022-04-11 18:13:03.000000000 +0000 @@ -58,6 +58,8 @@ fprintf(stderr,"Malloc failure in histogram init\n"); return(NULL); } + if (!bincount) + bincount = 1000; this->mybins = (unsigned int *) malloc(sizeof(unsigned int) * bincount); if (!this->mybins) { fprintf(stderr,"Malloc failure in histogram init b\n"); diff -Nru iperf-2.1.5+dfsg1/src/Launch.cpp iperf-2.1.7+dfsg1/src/Launch.cpp --- iperf-2.1.5+dfsg1/src/Launch.cpp 2021-12-05 20:49:56.000000000 +0000 +++ iperf-2.1.7+dfsg1/src/Launch.cpp 2022-04-11 18:13:03.000000000 +0000 @@ -158,7 +158,7 @@ theServer->RunUDP(); } else { if (isBounceBack(thread)) { - theServer->RunTcpBounceBack(); + theServer->RunBounceBackTCP(); } else { theServer->RunTCP(); } @@ -184,11 +184,7 @@ if ((thread->mThreads > 1) || isSumOnly(thread)) Iperf_push_host(thread); theClient->StartSynch(); - if (isBounceBack(thread)) { - theClient->RunBounceBackTCP(); - } else { - theClient->Run(); - } + theClient->Run(); } } @@ -398,6 +394,17 @@ itr->runNow = next; itr = next; } + if (isBounceBack(clients) && isCongest(clients)) { + Settings_Copy(clients, &next, 1); + if (next != NULL) { + setFullDuplex(next); + unsetBounceBack(next); + unsetEnhanced(next); + next->mTOS = 0; + itr->runNow = next; + itr = next; + } + } #else if (next != NULL) { // We don't have threads and we need to start a listener so diff -Nru iperf-2.1.5+dfsg1/src/Listener.cpp iperf-2.1.7+dfsg1/src/Listener.cpp --- iperf-2.1.5+dfsg1/src/Listener.cpp 2021-12-05 20:49:56.000000000 +0000 +++ iperf-2.1.7+dfsg1/src/Listener.cpp 2022-04-11 18:13:03.000000000 +0000 @@ -283,7 +283,7 @@ // This is required for accurate traffic statistics if (!isCompat(server) && (isConnectOnly(server) || !apply_client_settings(server))) { if (isConnectionReport(server) && !isSumOnly(server)) { - struct ReportHeader *reporthdr = InitConnectionReport(server, NULL); + struct ReportHeader *reporthdr = InitConnectionReport(server); struct ConnectionInfo *cr = static_cast(reporthdr->this_report); cr->connect_timestamp.tv_sec = server->accept_time.tv_sec; cr->connect_timestamp.tv_usec = server->accept_time.tv_usec; @@ -372,7 +372,7 @@ } setTransferID(server, 0); if (isConnectionReport(server) && !isSumOnly(server)) { - struct ReportHeader *reporthdr = InitConnectionReport(server, NULL); + struct ReportHeader *reporthdr = InitConnectionReport(server); struct ConnectionInfo *cr = static_cast(reporthdr->this_report); cr->connect_timestamp.tv_sec = server->accept_time.tv_sec; cr->connect_timestamp.tv_usec = server->accept_time.tv_usec; @@ -1063,7 +1063,7 @@ } if (!isUDP(server)) { int nread = 0; - nread = recvn(server->mSock, server->mBuf, keyoffset + keylen, 0); + nread = recvn(server->mSock, reinterpret_cast(&thiskey->value), keylen, 0); FAIL_errno((nread < (keyoffset + keylen)), "read key", server); } strncpy(server->mPermitKey, thiskey->value, MAX_PERMITKEY_LEN + 1); @@ -1096,7 +1096,13 @@ if (seqno != 1) { fprintf(stderr, "WARN: first received packet (id=%d) was not first sent packet, report start time will be off\n", seqno); } - setTripTime(server); + Timestamp now; + if (!isTxStartTime(server) && ((abs(now.getSecs() - server->sent_time.tv_sec)) > (MAXDIFFTIMESTAMPSECS + 1))) { + fprintf(stdout,"WARN: ignore --trip-times because client didn't provide valid start timestamp within %d seconds of now\n", MAXDIFFTIMESTAMPSECS); + unsetTripTime(server); + } else { + setTripTime(server); + } setEnhanced(server); } else if ((flags & HEADER_VERSION1) || (flags & HEADER_VERSION2) || (flags & HEADER_EXTEND)) { if ((flags & HEADER_VERSION1) && !(flags & HEADER_VERSION2)) { @@ -1144,6 +1150,7 @@ Timestamp now; if (!isTxStartTime(server) && ((abs(now.getSecs() - server->sent_time.tv_sec)) > (MAXDIFFTIMESTAMPSECS + 1))) { fprintf(stdout,"WARN: ignore --trip-times because client didn't provide valid start timestamp within %d seconds of now\n", MAXDIFFTIMESTAMPSECS); + unsetTripTime(server); } else { setTripTime(server); setEnhanced(server); @@ -1168,6 +1175,11 @@ } bool Listener::apply_client_settings_tcp (thread_Settings *server) { bool rc = false; +#if HAVE_TCP_STATS + if (!isUDP(mSettings)) { + gettcpinfo(server->mSock, &server->tcpinitstats); + } +#endif int nread = recvn(server->mSock, server->mBuf, sizeof(uint32_t), 0); char *readptr = server->mBuf; if (nread == 0) { @@ -1184,96 +1196,143 @@ struct client_tcp_testhdr *hdr = reinterpret_cast(server->mBuf); uint32_t flags = ntohl(hdr->base.flags); if (flags & HEADER_BOUNCEBACK) { + struct bounceback_hdr *bbhdr = reinterpret_cast(server->mBuf); setBounceBack(server); - } - uint16_t upperflags = 0; - int readlen; - // figure out the length of the test header - if ((readlen = Settings_ClientTestHdrLen(flags, server)) > 0) { - // read the test settings passed to the server by the client - nread += recvn(server->mSock, readptr, (readlen - (int) sizeof(uint32_t)), 0); - FAIL_errno((nread < readlen), "read tcp test info", server); - if (isPermitKey(mSettings)) { - if (!test_permit_key(flags, server, readlen)) { - rc = false; - goto DONE; - } - } else if (flags & HEADER_KEYCHECK) { + nread = recvn(server->mSock, readptr, sizeof(struct bounceback_hdr), 0); + if (nread != sizeof(struct bounceback_hdr)) { + WARN(1, "read bounce back header failed"); rc = false; - server->mKeyCheck = false; goto DONE; } - server->firstreadbytes = nread; - struct client_tcp_testhdr *hdr = reinterpret_cast(server->mBuf); - if ((flags & HEADER_VERSION1) && !(flags & HEADER_VERSION2)) { - if (flags & RUN_NOW) - server->mMode = kTest_DualTest; - else - server->mMode = kTest_TradeOff; - } - if (flags & HEADER_EXTEND) { - upperflags = htons(hdr->extend.upperflags); - server->mTOS = ntohs(hdr->extend.tos); - server->peer_version_u = ntohl(hdr->extend.version_u); - server->peer_version_l = ntohl(hdr->extend.version_l); - if (upperflags & HEADER_ISOCH) { - setIsochronous(server); + readptr += nread; + server->mBounceBackBytes = ntohl(bbhdr->bbsize); + server->mBounceBackHold = ntohl(bbhdr->bbhold); + uint16_t bbflags = ntohs(bbhdr->bbflags); + if (bbflags & HEADER_BBCLOCKSYNCED) { + setTripTime(server); + server->sent_time.tv_sec = ntohl(bbhdr->bbclientTx_ts.sec); + server->sent_time.tv_usec = ntohl(bbhdr->bbclientTx_ts.usec); + } + if (bbflags & HEADER_BBTOS) { + server->mTOS = ntohs(bbhdr->tos); + } +#if HAVE_DECL_TCP_QUICKACK + if (bbflags & HEADER_BBQUICKACK) { + setTcpQuickAck(server); + } +#endif + int remaining = server->mBounceBackBytes - (sizeof(struct bounceback_hdr) + sizeof(uint32_t)); + if (remaining < 0) { + WARN(1, "bounce back bytes too small"); + rc = false; + goto DONE; + } else if (remaining > 0) { + nread = recvn(server->mSock, readptr, remaining, 0); + if (nread != remaining) { + WARN(1, "read bounce back payload failed"); + rc = false; + goto DONE; } - if (upperflags & HEADER_EPOCH_START) { - server->txstart_epoch.tv_sec = ntohl(hdr->start_fq.start_tv_sec); - server->txstart_epoch.tv_usec = ntohl(hdr->start_fq.start_tv_usec); - Timestamp now; - if ((abs(now.getSecs() - server->txstart_epoch.tv_sec)) > (MAXDIFFTXSTART + 1)) { - fprintf(stdout,"WARN: ignore --txstart-time because client didn't provide valid start timestamp within %d seconds of now\n", MAXDIFFTXSTART); - unsetTxStartTime(server); - } else { - setTxStartTime(server); + } + Timestamp now; + bbhdr->bbserverRx_ts.sec = htonl(now.getSecs()); + bbhdr->bbserverRx_ts.usec = htonl(now.getUsecs()); + } else { + uint16_t upperflags = 0; + int readlen; + // figure out the length of the test header + if ((readlen = Settings_ClientTestHdrLen(flags, server)) > 0) { + // read the test settings passed to the server by the client + nread += recvn(server->mSock, readptr, (readlen - (int) sizeof(uint32_t)), 0); + FAIL_errno((nread < readlen), "read tcp test info", server); + if (isPermitKey(mSettings)) { + if (!test_permit_key(flags, server, readlen)) { + rc = false; + goto DONE; } + } else if (flags & HEADER_KEYCHECK) { + rc = false; + server->mKeyCheck = false; + goto DONE; } - if (upperflags & HEADER_TRIPTIME) { - Timestamp now; - server->sent_time.tv_sec = ntohl(hdr->start_fq.start_tv_sec); - server->sent_time.tv_usec = ntohl(hdr->start_fq.start_tv_usec); - if (!isTxStartTime(server) && ((abs(now.getSecs() - server->sent_time.tv_sec)) > (MAXDIFFTIMESTAMPSECS + 1))) { - fprintf(stdout,"WARN: ignore --trip-times because client didn't provide valid start timestamp within %d seconds of now\n", MAXDIFFTIMESTAMPSECS); - } else { - setTripTime(server); + server->firstreadbytes = nread; + struct client_tcp_testhdr *hdr = reinterpret_cast(server->mBuf); + if ((flags & HEADER_VERSION1) && !(flags & HEADER_VERSION2)) { + if (flags & RUN_NOW) + server->mMode = kTest_DualTest; + else + server->mMode = kTest_TradeOff; + } + if (flags & HEADER_EXTEND) { + upperflags = htons(hdr->extend.upperflags); + server->mTOS = ntohs(hdr->extend.tos); + server->peer_version_u = ntohl(hdr->extend.version_u); + server->peer_version_l = ntohl(hdr->extend.version_l); + if (upperflags & HEADER_ISOCH) { + setIsochronous(server); + } + if (upperflags & HEADER_EPOCH_START) { + server->txstart_epoch.tv_sec = ntohl(hdr->start_fq.start_tv_sec); + server->txstart_epoch.tv_usec = ntohl(hdr->start_fq.start_tv_usec); + Timestamp now; + if ((abs(now.getSecs() - server->txstart_epoch.tv_sec)) > (MAXDIFFTXSTART + 1)) { + fprintf(stdout,"WARN: ignore --txstart-time because client didn't provide valid start timestamp within %d seconds of now\n", MAXDIFFTXSTART); + unsetTxStartTime(server); + } else { + setTxStartTime(server); + } + } + if (upperflags & HEADER_TRIPTIME) { + Timestamp now; + server->sent_time.tv_sec = ntohl(hdr->start_fq.start_tv_sec); + server->sent_time.tv_usec = ntohl(hdr->start_fq.start_tv_usec); + if (!isTxStartTime(server) && ((abs(now.getSecs() - server->sent_time.tv_sec)) > (MAXDIFFTIMESTAMPSECS + 1))) { + fprintf(stdout,"WARN: ignore --trip-times because client didn't provide valid start timestamp within %d seconds of now\n", MAXDIFFTIMESTAMPSECS); + unsetTripTime(server); + } else { + setTripTime(server); + setEnhanced(server); + } + } + if (upperflags & HEADER_PERIODICBURST) { setEnhanced(server); + setFrameInterval(server); + setPeriodicBurst(server); + { + struct client_tcp_testhdr *hdr = reinterpret_cast(server->mBuf); + server->mFPS = ntohl(hdr->isoch_settings.FPSl); + server->mFPS += ntohl(hdr->isoch_settings.FPSu) / static_cast(rMillion); + } + if (!server->mFPS) { + server->mFPS = 1.0; + } } - } - if (upperflags & HEADER_PERIODICBURST) { - setEnhanced(server); - setFrameInterval(server); - setPeriodicBurst(server); - { - struct client_tcp_testhdr *hdr = reinterpret_cast(server->mBuf); - server->mFPS = ntohl(hdr->isoch_settings.FPSl); - server->mFPS += ntohl(hdr->isoch_settings.FPSu) / static_cast(rMillion); - } - if (!server->mFPS) { - server->mFPS = 1.0; - } - } - if (flags & HEADER_VERSION2) { - if (upperflags & HEADER_FULLDUPLEX) { - setFullDuplex(server); - setServerReverse(server); - } - if (upperflags & HEADER_REVERSE) { - server->mThreadMode=kMode_Client; - setServerReverse(server); + if (flags & HEADER_VERSION2) { + if (upperflags & HEADER_FULLDUPLEX) { + setFullDuplex(server); + setServerReverse(server); + } + if (upperflags & HEADER_REVERSE) { + server->mThreadMode=kMode_Client; + setServerReverse(server); + } } - } #if HAVE_DECL_TCP_NOTSENT_LOWAT - if ((isServerReverse(server) || isFullDuplex(server)) && (upperflags & HEADER_WRITEPREFETCH)) { - server->mWritePrefetch = ntohl(hdr->extend.TCPWritePrefetch); - if (server->mWritePrefetch > 0) { - setWritePrefetch(server); + if ((isServerReverse(server) || isFullDuplex(server)) && (upperflags & HEADER_WRITEPREFETCH)) { + server->mWritePrefetch = ntohl(hdr->extend.TCPWritePrefetch); + if (server->mWritePrefetch > 0) { + setWritePrefetch(server); + } + } +#endif +#if HAVE_DECL_TCP_QUICKACK + if (upperflags & HEADER_TCPQUICKACK) { + setTcpQuickAck(server); } - } #endif - if (upperflags & HEADER_BOUNCEBACK) { - setBounceBack(server); + if (upperflags & HEADER_BOUNCEBACK) { + setBounceBack(server); + } } } } diff -Nru iperf-2.1.5+dfsg1/src/Locale.c iperf-2.1.7+dfsg1/src/Locale.c --- iperf-2.1.5+dfsg1/src/Locale.c 2021-12-05 20:49:56.000000000 +0000 +++ iperf-2.1.7+dfsg1/src/Locale.c 2022-04-11 18:13:03.000000000 +0000 @@ -75,7 +75,7 @@ --hide-ips hide ip addresses and host names within outputs\n\ -i, --interval # seconds between periodic bandwidth reports\n\ -l, --len #[kmKM] length of buffer in bytes to read or write (Defaults: TCP=128K, v4 UDP=1470, v6 UDP=1450)\n\ - -m, --print_mss print TCP maximum segment size (MTU - TCP/IP header)\n\ + -m, --print_mss print TCP maximum segment size\n\ -o, --output output the report or error message to this specified file\n\ -p, --port # client/server port to listen/send on and to connect\n\ --permit-key permit key to be used to verify client and server (TCP only)\n\ @@ -87,7 +87,7 @@ #endif " -B, --bind [:][%] bind to , ip addr (including multicast address) and optional port and device\n\ -C, --compatibility for use with older versions does not sent extra msgs\n\ - -M, --mss # set TCP maximum segment size (MTU - 40 bytes)\n\ + -M, --mss # set TCP maximum segment size using TCP_MAXSEG\n\ -N, --nodelay set TCP no delay, disabling Nagle's Algorithm\n\ -S, --tos # set the socket's IP_TOS (byte) field\n\ -Z, --tcp-congestion set TCP congestion control algorithm (Linux only)\n\ @@ -115,6 +115,11 @@ const char usage_long2[] = "\ \n\ Client specific:\n\ + --bounceback request a bounceback test (use -l for size, defaults to 100 bytes)\n\ + --bounceback-congest request a concurrent full-duplex TCP stream\n\ + --bounceback-hold request the server to insert a delay of n milliseconds between its read and write\n\ + --bounceback-period request the client schedule a send every n milliseconds\n\ + --bounceback-no-quickack request the server not set the TCP_QUICKACK socket option (disabling TCP ACK delays) during a bounceback test\n\ -c, --client run in client mode, connecting to \n\ --connect-only run a connect only test\n\ --connect-retries # number of times to retry tcp connect\n\ @@ -133,7 +138,9 @@ --no-udp-fin No final server to client stats at end of UDP test\n\ -n, --num #[kmgKMG] number of bytes to transmit (instead of -t)\n\ -r, --tradeoff Do a fullduplexectional test individually\n\ + --tcp-quickack set the socket's TCP_QUICKACK option (off by default)\n\ --tcp-write-prefetch set the socket's TCP_NOTSENT_LOWAT value in bytes and use event based writes\n\ + --tcp-write-times measure the socket write times at the application level\n\ -t, --time # time in seconds to transmit for (default 10 secs)\n\ --trip-times enable end to end measurements (requires client and server clock sync)\n\ --txdelay-time time in seconds to hold back after connect and before first write\n\ @@ -261,7 +268,10 @@ "Bursting: %s every %0.2f seconds\n"; const char client_bounceback[] = -"Bounce-back size = %s\n"; +"Bounce-back test (size=%s) (server hold req=%d usecs & tcp_quickack)\n"; + +const char client_bounceback_noqack[] = +"Bounce-back test (size=%s) (server hold req=%d usecs)\n"; const char server_burstperiod[] = "Burst wait timeout set to (2 * %0.2f) seconds (use --burst-period= to change)\n"; @@ -345,16 +355,31 @@ const char report_triptime_enhanced_format[] = "%s" IPERFTimeFrmt " trip-time (3WHS done->fin+finack) = %.4f sec\n"; -#if HAVE_DECL_TCP_NOTSENT_LOWAT -const char report_write_enhanced_drain_header[] = -"[ ID] Interval" IPERFTimeSpace "Transfer Bandwidth Write/Err Rtry Cwnd/RTT NetPwr Drain avg/min/max/stdev (cnt)\n"; +#if HAVE_TCP_STATS +const char report_client_bb_bw_header[] = +"[ ID] Interval" IPERFTimeSpace "Transfer Bandwidth BB cnt(cnt/s)=avg/min/max/stdev Rtry Cwnd/RTT\n"; + +const char report_client_bb_bw_format[] = +"%s" IPERFTimeFrmt " sec %ss %ss/sec %d(%d)=%.3f/%.3f/%.3f/%.3f ms %4d %4dK/%u us\n"; +#else +const char report_client_bb_bw_header[] = +"[ ID] Interval" IPERFTimeSpace "Transfer Bandwidth BB cnt(cnt/s)=avg/min/max/stdev\n"; + +const char report_client_bb_bw_format[] = +"%s" IPERFTimeFrmt " sec %ss %ss/sec %d(%d)=%.3f/%.3f/%.3f/%.3f ms\n"; +#endif +const char report_client_bb_bw_triptime_format[] = +"%s" IPERFTimeFrmt " sec OWD Delays (ms) Cnt=%d(%d) To=%.3f/%.3f/%.3f/%.3f From=%.3f/%.3f/%.3f/%.3f Asymmetry=%.3f/%.3f/%.3f/%.3f\n"; + +const char report_write_enhanced_write_header[] = +"[ ID] Interval" IPERFTimeSpace "Transfer Bandwidth Write/Err Rtry Cwnd/RTT NetPwr write-times avg/min/max/stdev (cnt)\n"; -const char report_write_enhanced_drain_format[] = +const char report_write_enhanced_write_format[] = "%s" IPERFTimeFrmt " sec %ss %ss/sec %d/%d %10d %8dK/%u us %s %.3f/%.3f/%.3f/%.3f ms (%d)\n"; -const char report_write_enhanced_nocwnd_drain_format[] = +const char report_write_enhanced_nocwnd_write_format[] = "%s" IPERFTimeFrmt " sec %ss %ss/sec %d/%d %10d NA/%u us %s %.3f/%.3f/%.3f/%.3f ms (%d)\n"; -#endif + #if HAVE_TCP_STATS const char report_bw_write_enhanced_header[] = @@ -379,6 +404,7 @@ "%s" IPERFTimeFrmt " sec %ss %ss/sec %d/%d %10d NA/%u us %9" PRIuMAX "/%" PRIuMAX "/%" PRIuMAX " %s\n"; #else + const char report_bw_write_enhanced_header[] = "[ ID] Interval" IPERFTimeSpace "Transfer Bandwidth Write/Err\n"; @@ -393,7 +419,6 @@ const char report_write_enhanced_isoch_format[] = "%s" IPERFTimeFrmt " sec %ss %ss/sec %d/%d %9" PRIuMAX "/%" PRIuMAX "/%" PRIuMAX "\n"; - #endif const char report_sumcnt_bw_write_enhanced_header[] = @@ -488,6 +513,26 @@ "%s" IPERFTimeFrmt " sec %ss %ss/sec %6.3f ms (%.2g%%) %d=%d:%d:%d:%d:%d:%d:%d:%d %s\n"; const char report_burst_read_tcp_final_format[] = +"%s" IPERFTimeFrmt " sec %ss %ss/sec %.3f/%.3f/%.3f/%.3f ms %d=%d:%d:%d:%d:%d:%d:%d:%d\n"; +#if HAVE_TCP_STATS +const char report_burst_write_tcp_header[] = +"[ ID] Burst (start-end)" IPERFFTimeSpace "Transfer Bandwidth XferTime Write/Err Rtry Cwnd/RTT NetPwr\n"; + +const char report_burst_write_tcp_format[] = +"%s" IPERFTimeFrmt " sec %ss %ss/sec %6.3f ms %d/%d %10d %8dK/%u %s\n"; + +#else +const char report_burst_write_tcp_header[] = +"[ ID] Burst (start-end)" IPERFFTimeSpace "Transfer Bandwidth XferTime Write/Err\n"; + +const char report_burst_write_tcp_format[] = +"%s" IPERFTimeFrmt " sec %ss %ss/sec %6.3f ms %d/%d\n"; +#endif + +const char report_burst_write_tcp_nocwnd_format[] = +"%s" IPERFTimeFrmt " sec %ss %ss/sec %6.3f ms %d/%d %10d NA/%u %s\n"; + +const char report_burst_write_tcp_final_format[] = "%s" IPERFTimeFrmt " sec %ss %ss/sec %d=%d:%d:%d:%d:%d:%d:%d:%d\n"; /* ------------------------------------------------------------------- @@ -544,8 +589,11 @@ const char report_mss_unsupported[] = "%sMSS and MTU size unknown (TCP_MAXSEG not supported)\n"; +const char report_default_mss[] = +"MSS size %d bytes\n"; + const char report_mss[] = -"%sMSS size %d bytes\n"; +"MSS req size %d bytes (per TCP_MAXSEG)\n"; const char report_datagrams[] = "[%3d] Sent %d datagrams\n"; diff -Nru iperf-2.1.5+dfsg1/src/main.cpp iperf-2.1.7+dfsg1/src/main.cpp --- iperf-2.1.5+dfsg1/src/main.cpp 2021-12-05 20:49:56.000000000 +0000 +++ iperf-2.1.7+dfsg1/src/main.cpp 2022-04-11 18:13:03.000000000 +0000 @@ -230,6 +230,8 @@ mbuflen += TAPBYTESSLOP; #endif ext_gSettings->mBuf = new char[mbuflen]; + memset(ext_gSettings->mBuf, 0, mbuflen); + unsetReport(ext_gSettings); switch (ext_gSettings->mThreadMode) { diff -Nru iperf-2.1.5+dfsg1/src/PerfSocket.cpp iperf-2.1.7+dfsg1/src/PerfSocket.cpp --- iperf-2.1.5+dfsg1/src/PerfSocket.cpp 2021-12-05 20:49:56.000000000 +0000 +++ iperf-2.1.7+dfsg1/src/PerfSocket.cpp 2022-04-11 18:13:03.000000000 +0000 @@ -254,19 +254,6 @@ reinterpret_cast(&bytecnt), len); WARN_errno(rc == SOCKET_ERROR, "setsockopt TCP_NOTSENT_LOWAT"); } - if (isTcpDrain(inSettings)) { - int value; - int rc; - Socklen_t len = sizeof(value); - value = 4; // a small value, zero disables - rc = setsockopt(inSettings->mSock, IPPROTO_TCP, TCP_NOTSENT_LOWAT, - reinterpret_cast(&value), len); - WARN_errno(rc == SOCKET_ERROR, "setsockopt TCP_NOTSENT_LOWAT"); - value = 1; - rc = setsockopt(inSettings->mSock, IPPROTO_TCP, TCP_NODELAY, - reinterpret_cast(&value), len); - WARN_errno(rc == SOCKET_ERROR, "setsockopt TCP_NODELAY"); - } #endif } @@ -337,11 +324,15 @@ #endif // set IP TOS (type-of-service) field if (isOverrideTOS(mSettings) || (tos > 0)) { - int tos = tos; - Socklen_t len = sizeof(tos); + int reqtos = tos; + Socklen_t len = sizeof(reqtos); int rc = setsockopt(mSettings->mSock, IPPROTO_IP, IP_TOS, - reinterpret_cast(&tos), len); + reinterpret_cast(&reqtos), len); WARN_errno(rc == SOCKET_ERROR, "setsockopt IP_TOS"); + rc = getsockopt(mSettings->mSock, IPPROTO_IP, IP_TOS, + reinterpret_cast(&reqtos), &len); + WARN_errno(rc == SOCKET_ERROR, "getsockopt IP_TOS"); + WARN((reqtos != tos), "IP_TOS setting failed"); } #endif } @@ -365,5 +356,90 @@ #endif } +/* ------------------------------------------------------------------- + * If inMSS > 0, set the TCP maximum segment size for inSock. + * Otherwise leave it as the system default. + * ------------------------------------------------------------------- */ + +const char warn_mss_fail[] = "\ +WARNING: attempt to set TCP maxmimum segment size to %d failed\n"; + +void setsock_tcp_mss (int inSock, int inMSS) { +#if HAVE_DECL_TCP_MAXSEG + int rc; + int newMSS; + Socklen_t len; + + assert(inSock != INVALID_SOCKET); + + if (inMSS > 0) { + /* set */ + newMSS = inMSS; + len = sizeof(newMSS); + rc = setsockopt(inSock, IPPROTO_TCP, TCP_MAXSEG, (char*) &newMSS, len); + if (rc == SOCKET_ERROR) { + fprintf(stderr, warn_mss_fail, newMSS); + return; + } + } +#endif +} /* end setsock_tcp_mss */ + +/* ------------------------------------------------------------------- + * returns the TCP maximum segment size + * ------------------------------------------------------------------- */ + +int getsock_tcp_mss (int inSock) { + int theMSS = -1; +#if HAVE_DECL_TCP_MAXSEG + int rc; + Socklen_t len; + assert(inSock >= 0); + + /* query for MSS */ + len = sizeof(theMSS); + rc = getsockopt(inSock, IPPROTO_TCP, TCP_MAXSEG, (char*)&theMSS, &len); + WARN_errno(rc == SOCKET_ERROR, "getsockopt TCP_MAXSEG"); +#endif + return theMSS; +} /* end getsock_tcp_mss */ + +#ifdef DEFAULT_PAYLOAD_LEN_PER_MTU_DISCOVERY +#define UDPMAXSIZE ((1024 * 64) - 64) // 16 bit field for UDP +void checksock_max_udp_payload (struct thread_Settings *inSettings) { +#if HAVE_DECL_SIOCGIFMTU + struct ifreq ifr; + if (!isBuflenSet(inSettings) && inSettings->mIfrname) { + strncpy(ifr.ifr_name, inSettings->mIfrname, (size_t) (IFNAMSIZ - 1)); + if (!ioctl(inSettings->mSock, SIOCGIFMTU, &ifr)) { + int max; + if (!isIPV6(inSettings)) { + max = ifr.ifr_mtu - IPV4HDRLEN - UDPHDRLEN; + } else { + max = ifr.ifr_mtu - IPV6HDRLEN - UDPHDRLEN; + } + if ((max > 0) && (max != inSettings->mBufLen)) { + if (max > UDPMAXSIZE) { + max = UDPMAXSIZE; + } + if (max > inSettings->mBufLen) { + char *tmp = new char[max]; + assert(tmp!=NULL); + if (tmp) { + pattern(tmp, max); + memcpy(tmp, inSettings->mBuf, inSettings->mBufLen); + DELETE_ARRAY(inSettings->mBuf); + inSettings->mBuf = tmp; + inSettings->mBufLen = max; + } + } else { + inSettings->mBufLen = max; + } + } + } + } +#endif +} +#endif // end SetSocketOptions diff -Nru iperf-2.1.5+dfsg1/src/Reporter.c iperf-2.1.7+dfsg1/src/Reporter.c --- iperf-2.1.5+dfsg1/src/Reporter.c 2021-12-05 20:49:56.000000000 +0000 +++ iperf-2.1.7+dfsg1/src/Reporter.c 2022-04-11 18:13:03.000000000 +0000 @@ -83,7 +83,18 @@ static void reporter_reset_transfer_stats_server_udp(struct TransferInfo *stats); static void reporter_reset_transfer_stats_server_tcp(struct TransferInfo *stats); -static void reporter_mmm_update (struct MeanMinMaxStats *stats, double value); +// code for welfornd's algorithm to produce running mean/min/max/var +static void reporter_update_mmm (struct MeanMinMaxStats *stats, double value); +static void reporter_reset_mmm (struct MeanMinMaxStats *stats); + + +// one way delay (OWD) calculations +static void reporter_handle_packet_oneway_transit(struct TransferInfo *stats, struct ReportStruct *packet); +static void reporter_handle_frame_isoch_oneway_transit(struct TransferInfo *stats, struct ReportStruct *packet); +static void reporter_handle_txmsg_oneway_transit(struct TransferInfo *stats, struct ReportStruct *packet); +static void reporter_handle_rxmsg_oneway_transit(struct TransferInfo *stats, struct ReportStruct *packet); + +static inline void reporter_compute_packet_pps (struct TransferInfo *stats, struct ReportStruct *packet); #if HAVE_TCP_STATS static inline void reporter_handle_packet_tcpistats(struct ReporterData *data, struct ReportStruct *packet); @@ -142,12 +153,13 @@ #endif #if HAVE_TCP_STATS struct TransferInfo *stats = &data->info; + packet->tcpstats.isValid = false; if (stats->isEnableTcpInfo) { - if (!TimeZero(stats->ts.nextTCPStampleTime) && (TimeDifference(stats->ts.nextTCPStampleTime, packet->packetTime) < 0)) { - gettcpinfo(data->info.common->socket, packet); - TimeAdd(stats->ts.nextTCPStampleTime, stats->ts.intervalTime); + if (!TimeZero(stats->ts.nextTCPSampleTime) && (TimeDifference(stats->ts.nextTCPSampleTime, packet->packetTime) < 0)) { + gettcpinfo(data->info.common->socket, &packet->tcpstats); + TimeAdd(stats->ts.nextTCPSampleTime, stats->ts.intervalTime); } else { - gettcpinfo(data->info.common->socket, packet); + gettcpinfo(data->info.common->socket, &packet->tcpstats); } } #endif @@ -199,7 +211,7 @@ // tcpi stats are sampled on a final packet struct TransferInfo *stats = &report->info; if (stats->isEnableTcpInfo) { - gettcpinfo(report->info.common->socket, finalpacket); + gettcpinfo(report->info.common->socket, &finalpacket->tcpstats); } #endif // clear the reporter done predicate @@ -308,6 +320,7 @@ Condition_Lock(ReportCond); // check the jobq for empty if (ReportRoot == NULL) { + sInterupted = 0; // reset flags in reporter thread emtpy context // The reporter is starting from an empty state // so set the load detect to trigger an initial delay if (!isSingleUDP(inSettings)) { @@ -365,7 +378,7 @@ * return (mean, variance, sampleVariance) * */ -static void reporter_mmm_update (struct MeanMinMaxStats *stats, double value) { +static void reporter_update_mmm (struct MeanMinMaxStats *stats, double value) { assert(stats != NULL); stats->cnt++; if (stats->cnt == 1) { @@ -382,15 +395,23 @@ stats->vd = value - stats->mean; stats->mean += (stats->vd / stats->cnt); stats->m2 += stats->vd * (value - stats->mean); -// printf("*****m2=%f, mmm = %f/%f\n", stats->m2, stats->vd, (value - stats->mean)); // mean min max tests if (value < stats->min) stats->min = value; if (value > stats->max) stats->max = value; } -// printf("*****val=%f, mmm = %d/%f/%f/%f/%f/%f/%f\n", value, stats->cnt, stats->sum, stats->vd, stats->mean, stats->m2, stats->min, stats->max); + // fprintf(stderr,"**** mmm(%d) val/sum=%f/%f mmm=%f/%f/%f/%f\n", stats->cnt, value, stats->sum, stats->mean, stats->min, stats->max, stats->m2); } +static void reporter_reset_mmm (struct MeanMinMaxStats *stats) { + stats->min = FLT_MAX; + stats->max = FLT_MIN; + stats->sum = 0; + stats->vd = 0; + stats->mean = 0; + stats->m2 = 0; + stats->cnt = 0; +}; /* * This function is the loop that the reporter thread processes @@ -611,8 +632,8 @@ assert(creport!=NULL); if (!isCompat(creport->common) && (creport->common->ThreadMode == kMode_Client) && myConnectionReport) { // Clients' connect times will be inputs to the overall connect stats - if (creport->init_cond.connecttime > 0.0) { - reporter_mmm_update(&myConnectionReport->connect_times, creport->init_cond.connecttime); + if (creport->tcpinitstats.connecttime > 0.0) { + reporter_update_mmm(&myConnectionReport->connect_times, creport->tcpinitstats.connecttime); } else { myConnectionReport->connect_times.err++; } @@ -654,8 +675,7 @@ void reporter_transfer_protocol_null (struct ReporterData *data, int final){ } -inline void reporter_handle_packet_pps (struct ReporterData *data, struct ReportStruct *packet) { - struct TransferInfo *stats = &data->info; +static inline void reporter_compute_packet_pps (struct TransferInfo *stats, struct ReportStruct *packet) { if (!packet->emptyreport) { stats->total.Datagrams.current++; stats->total.IPG.current++; @@ -667,92 +687,83 @@ #endif } -// Variance uses the Welford inline algorithm, mean is also inline -static inline double reporter_handle_packet_oneway_transit (struct ReporterData *data, struct ReportStruct *packet) { - struct TransferInfo *stats = &data->info; +static void reporter_handle_packet_oneway_transit (struct TransferInfo *stats, struct ReportStruct *packet) { // Transit or latency updates done inline below double transit = TimeDifference(packet->packetTime, packet->sentTime); - double usec_transit = transit * 1e6; - if (stats->latency_histogram) { histogram_insert(stats->latency_histogram, transit, NULL); } + double deltaTransit; + // from RFC 1889, Real Time Protocol (RTP) + // J = J + ( | D(i-1,i) | - J ) / + // Compute jitter + deltaTransit = transit - stats->transit.current.last; + if (deltaTransit < 0.0) { + deltaTransit = -deltaTransit; + } + stats->jitter += (deltaTransit - stats->jitter) / (16.0); + // Compute end/end delay stats + reporter_update_mmm(&stats->transit.total, transit); + reporter_update_mmm(&stats->transit.current, transit); + stats->transit.current.last = transit; +} - if (stats->transit.totcntTransit == 0) { - // Very first packet - stats->transit.minTransit = transit; - stats->transit.maxTransit = transit; - stats->transit.sumTransit = transit; - stats->transit.cntTransit = 1; - stats->transit.totminTransit = transit; - stats->transit.totmaxTransit = transit; - stats->transit.totsumTransit = transit; - stats->transit.totcntTransit = 1; - // For variance, working units is microseconds - stats->transit.vdTransit = usec_transit; - stats->transit.meanTransit = usec_transit; - stats->transit.m2Transit = usec_transit * usec_transit; - stats->transit.totvdTransit = usec_transit; - stats->transit.totmeanTransit = usec_transit; - stats->transit.totm2Transit = usec_transit * usec_transit; - } else { - double deltaTransit; - // from RFC 1889, Real Time Protocol (RTP) - // J = J + ( | D(i-1,i) | - J ) / - // Compute jitter - deltaTransit = transit - stats->transit.lastTransit; - if (deltaTransit < 0.0) { - deltaTransit = -deltaTransit; - } - stats->jitter += (deltaTransit - stats->jitter) / (16.0); - // Compute end/end delay stats - stats->transit.sumTransit += transit; - stats->transit.cntTransit++; - stats->transit.totsumTransit += transit; - stats->transit.totcntTransit++; - // mean min max tests - if (transit < stats->transit.minTransit) { - stats->transit.minTransit=transit; - } - if (transit < stats->transit.totminTransit) { - stats->transit.totminTransit=transit; +static void reporter_handle_frame_isoch_oneway_transit (struct TransferInfo *stats, struct ReportStruct *packet) { + // printf("fid=%lu bs=%lu remain=%lu\n", packet->frameID, packet->burstsize, packet->remaining); + if (packet->frameID && packet->transit_ready) { + int framedelta=0; + // very first isochronous frame + if (!stats->isochstats.frameID) { + stats->isochstats.framecnt.current=packet->frameID; } - if (transit > stats->transit.maxTransit) { - stats->transit.maxTransit=transit; + // perform client and server frame based accounting + if ((framedelta = (packet->frameID - stats->isochstats.frameID))) { + stats->isochstats.framecnt.current++; + if (framedelta > 1) { + if (stats->common->ThreadMode == kMode_Server) { + int lost = framedelta - (packet->frameID - packet->prevframeID); + stats->isochstats.framelostcnt.current += lost; + } else { + stats->isochstats.framelostcnt.current += (framedelta-1); + stats->isochstats.slipcnt.current++; + } + } } - if (transit > stats->transit.totmaxTransit) { - stats->transit.totmaxTransit=transit; + // peform frame latency checks + if (stats->framelatency_histogram) { + // first packet of a burst and not a duplicate + if ((packet->burstsize == packet->remaining) && (stats->matchframeID!=packet->frameID)) { + stats->matchframeID=packet->frameID; + } + if ((packet->packetLen == packet->remaining) && (packet->frameID == stats->matchframeID)) { + // last packet of a burst (or first-last in case of a duplicate) and frame id match + double frametransit = TimeDifference(packet->packetTime, packet->isochStartTime) \ + - ((packet->burstperiod * (packet->frameID - 1)) / 1000000.0); + histogram_insert(stats->framelatency_histogram, frametransit, NULL); + stats->matchframeID = 0; // reset the matchid so any potential duplicate is ignored + } } - // For variance, working units is microseconds - // variance interval - stats->transit.vdTransit = usec_transit - stats->transit.meanTransit; - stats->transit.meanTransit = stats->transit.meanTransit + (stats->transit.vdTransit / stats->transit.cntTransit); - stats->transit.m2Transit = stats->transit.m2Transit + (stats->transit.vdTransit * (usec_transit - stats->transit.meanTransit)); - // variance total - stats->transit.totvdTransit = usec_transit - stats->transit.totmeanTransit; - stats->transit.totmeanTransit = stats->transit.totmeanTransit + (stats->transit.totvdTransit / stats->transit.totcntTransit); - stats->transit.totm2Transit = stats->transit.totm2Transit + (stats->transit.totvdTransit * (usec_transit - stats->transit.totmeanTransit)); + stats->isochstats.frameID = packet->frameID; } - stats->transit.lastTransit = transit; - return (transit); } -static inline void reporter_handle_burst_tcp_server_transit (struct ReporterData *data, struct ReportStruct *packet) { - struct TransferInfo *stats = &data->info; +static void reporter_handle_rxmsg_oneway_transit (struct TransferInfo *stats, struct ReportStruct *packet) { // very first burst if (!stats->isochstats.frameID) { stats->isochstats.frameID = packet->frameID; } if (packet->frameID && packet->transit_ready) { - double transit = reporter_handle_packet_oneway_transit(data, packet); + double transit = TimeDifference(packet->packetTime, packet->sentTime); + reporter_update_mmm(&stats->transit.total, transit); + reporter_update_mmm(&stats->transit.current, transit); + if (stats->framelatency_histogram) { + histogram_insert(stats->framelatency_histogram, transit, &packet->sentTime); + } if (!TimeZero(stats->ts.prevpacketTime)) { double delta = TimeDifference(packet->sentTime, stats->ts.prevpacketTime); stats->IPGsum += delta; } stats->ts.prevpacketTime = packet->sentTime; - if (stats->framelatency_histogram) { - histogram_insert(stats->framelatency_histogram, transit, isTripTime(stats->common) ? &packet->sentTime : NULL); - } stats->isochstats.frameID++; // RJM fix this overload stats->burstid_transition = true; // printf("***Burst id = %ld, transit = %f\n", packet->frameID, stats->transit.lastTransit); @@ -764,62 +775,95 @@ } } -static inline void reporter_handle_burst_tcp_client_transit (struct ReporterData *data, struct ReportStruct *packet) { - struct TransferInfo *stats = &data->info; +static inline void reporter_handle_txmsg_oneway_transit (struct TransferInfo *stats, struct ReportStruct *packet) { // very first burst if (!stats->isochstats.frameID) { stats->isochstats.frameID = packet->frameID; } - if (stats->burstid_transition && packet->frameID && packet->transit_ready) { - stats->burstid_transition = false; + if (!TimeZero(stats->ts.prevpacketTime)) { + double delta = TimeDifference(packet->sentTime, stats->ts.prevpacketTime); + stats->IPGsum += delta; + } + if (packet->transit_ready) { + reporter_handle_packet_oneway_transit(stats, packet); // printf("***Burst id = %ld, transit = %f\n", packet->frameID, stats->transit.lastTransit); - } else if (isIsochronous(stats->common) && !stats->burstid_transition) { - stats->burstid_transition = true; - if (packet->frameID && (packet->frameID != (stats->isochstats.frameID + 1))) { - fprintf(stderr,"%sError: expected burst id %u but got %" PRIdMAX "\n", \ - stats->common->transferIDStr, stats->isochstats.frameID + 1, packet->frameID); + if (isIsochronous(stats->common)) { + if (packet->frameID && (packet->frameID != (stats->isochstats.frameID + 1))) { + fprintf(stderr,"%sError: expected burst id %u but got %" PRIdMAX "\n", \ + stats->common->transferIDStr, stats->isochstats.frameID + 1, packet->frameID); + } + stats->isochstats.frameID = packet->frameID; } - stats->isochstats.frameID = packet->frameID; } } +// This is done in reporter thread context -inline void reporter_handle_packet_isochronous (struct ReporterData *data, struct ReportStruct *packet) { +void reporter_handle_packet_client (struct ReporterData *data, struct ReportStruct *packet) { struct TransferInfo *stats = &data->info; - // printf("fid=%lu bs=%lu remain=%lu\n", packet->frameID, packet->burstsize, packet->remaining); - if (packet->frameID && packet->transit_ready) { - int framedelta=0; - // very first isochronous frame - if (!stats->isochstats.frameID) { - stats->isochstats.framecnt.current=packet->frameID; + stats->ts.packetTime = packet->packetTime; + if (!packet->emptyreport) { + stats->total.Bytes.current += packet->packetLen; + if (packet->errwrite && (packet->errwrite != WriteErrNoAccount)) { + stats->sock_callstats.write.WriteErr++; + stats->sock_callstats.write.totWriteErr++; } - // perform client and server frame based accounting - if ((framedelta = (packet->frameID - stats->isochstats.frameID))) { - stats->isochstats.framecnt.current++; - if (framedelta > 1) { - if (stats->common->ThreadMode == kMode_Server) { - int lost = framedelta - (packet->frameID - packet->prevframeID); - stats->isochstats.framelostcnt.current += lost; - } else { - stats->isochstats.framelostcnt.current += (framedelta-1); - stats->isochstats.slipcnt.current++; - } - } + // These are valid packets that need standard iperf accounting + stats->sock_callstats.write.WriteCnt += packet->writecnt; + stats->sock_callstats.write.totWriteCnt += packet->writecnt; + if (isIsochronous(stats->common)) { + reporter_handle_frame_isoch_oneway_transit(stats, packet); + } else if (isPeriodicBurst(stats->common)) { + reporter_handle_txmsg_oneway_transit(stats, packet); } - // peform frame latency checks - if (stats->framelatency_histogram) { - // first packet of a burst and not a duplicate - if ((packet->burstsize == packet->remaining) && (stats->matchframeID!=packet->frameID)) { - stats->matchframeID=packet->frameID; - } - if ((packet->packetLen == packet->remaining) && (packet->frameID == stats->matchframeID)) { - // last packet of a burst (or first-last in case of a duplicate) and frame id match - double frametransit = TimeDifference(packet->packetTime, packet->isochStartTime) \ - - ((packet->burstperiod * (packet->frameID - 1)) / 1000000.0); - histogram_insert(stats->framelatency_histogram, frametransit, NULL); - stats->matchframeID = 0; // reset the matchid so any potential duplicate is ignored + if (isTcpWriteTimes(stats->common) && !isUDP(stats->common) && (packet->write_time > 0)) { + reporter_update_mmm(&stats->write_mmm.current, (double) packet->write_time); + reporter_update_mmm(&stats->write_mmm.total, (double) packet->write_time); + if (stats->write_histogram ) { + histogram_insert(stats->write_histogram, (1e-6 * packet->write_time), &packet->packetTime); } } - stats->isochstats.frameID = packet->frameID; + } + if (isUDP(stats->common)) { + stats->PacketID = packet->packetID; + reporter_compute_packet_pps(stats, packet); + } else if (packet->transit_ready) { + if (isIsochronous(stats->common) && packet->frameID) { + reporter_handle_frame_isoch_oneway_transit(stats, packet); + } else if (isPeriodicBurst(stats->common) || isTripTime(stats->common)) { + reporter_handle_rxmsg_oneway_transit(stats, packet); + } + } +} + +void reporter_handle_packet_bb_client (struct ReporterData *data, struct ReportStruct *packet) { + struct TransferInfo *stats = &data->info; + stats->ts.packetTime = packet->packetTime; + if (!packet->emptyreport && (packet->packetLen > 0)) { + stats->total.Bytes.current += packet->packetLen; + double bbrtt = TimeDifference(packet->packetTime, packet->sentTime); + double bbowdto = TimeDifference(packet->sentTimeRX, packet->sentTime); + double bbowdfro = TimeDifference(packet->packetTime, packet->sentTimeTX); + double asym = bbowdfro - bbowdto; +#if 0 + fprintf(stderr, "BB Debug: ctx=%lx.%lx srx=%lx.%lx stx=%lx.%lx crx=%lx.%lx\n", packet->sentTime.tv_sec, packet->sentTime.tv_usec, packet->sentTimeRX.tv_sec, packet->sentTimeRX.tv_usec, packet->sentTimeTX.tv_sec, packet->sentTimeTX.tv_usec, packet->packetTime.tv_sec, packet->packetTime.tv_usec); + fprintf(stderr, "BB Debug: ctx=%ld.%ld srx=%ld.%ld stx=%ld.%ld crx=%ld.%ld\n", packet->sentTime.tv_sec, packet->sentTime.tv_usec, packet->sentTimeRX.tv_sec, packet->sentTimeRX.tv_usec, packet->sentTimeTX.tv_sec, packet->sentTimeTX.tv_usec, packet->packetTime.tv_sec, packet->packetTime.tv_usec); +#endif + reporter_update_mmm(&stats->bbrtt.current, bbrtt); + reporter_update_mmm(&stats->bbrtt.total, bbrtt); + reporter_update_mmm(&stats->bbowdto.total, bbowdto); + reporter_update_mmm(&stats->bbowdfro.total, bbowdfro); + reporter_update_mmm(&stats->bbasym.total, fabs(asym)); + if (stats->bbrtt_histogram) { + histogram_insert(stats->bbrtt_histogram, bbrtt, NULL); + } + } +} + +void reporter_handle_packet_bb_server (struct ReporterData *data, struct ReportStruct *packet) { + struct TransferInfo *stats = &data->info; + stats->ts.packetTime = packet->packetTime; + if (!packet->emptyreport && (packet->packetLen > 0)) { + stats->total.Bytes.current += packet->packetLen; } } @@ -836,25 +880,25 @@ stats->sock_callstats.read.bins[bin]++; stats->sock_callstats.read.totbins[bin]++; } - if (isPeriodicBurst(stats->common) || isTripTime(stats->common)) - reporter_handle_burst_tcp_server_transit(data, packet); + if (packet->transit_ready) { + if (isIsochronous(stats->common) && packet->frameID) { + reporter_handle_frame_isoch_oneway_transit(stats, packet); + } else if (isPeriodicBurst(stats->common) || isTripTime(stats->common)) { + reporter_handle_rxmsg_oneway_transit(stats, packet); + } + } } } inline void reporter_handle_packet_server_udp (struct ReporterData *data, struct ReportStruct *packet) { struct TransferInfo *stats = &data->info; stats->ts.packetTime = packet->packetTime; - if (packet->emptyreport && (stats->transit.cntTransit == 0)) { + if (packet->emptyreport && (stats->transit.current.cnt == 0)) { // This is the case when empty reports // cross the report interval boundary // Hence, set the per interval min to infinity // and the per interval max and sum to zero - stats->transit.minTransit = FLT_MAX; - stats->transit.maxTransit = FLT_MIN; - stats->transit.sumTransit = 0; - stats->transit.vdTransit = 0; - stats->transit.meanTransit = 0; - stats->transit.m2Transit = 0; + reporter_reset_mmm(&stats->transit.current); } else if (packet->packetID > 0) { stats->total.Bytes.current += packet->packetLen; // These are valid packets that need standard iperf accounting @@ -887,9 +931,15 @@ if (packet->packetID > stats->PacketID) { stats->PacketID = packet->packetID; } - reporter_handle_packet_pps(data, packet); - reporter_handle_packet_oneway_transit(data, packet); - reporter_handle_packet_isochronous(data, packet); + reporter_compute_packet_pps(stats, packet); + reporter_handle_packet_oneway_transit(stats, packet); + if (packet->transit_ready) { + if (isIsochronous(stats->common) && packet->frameID) { + reporter_handle_frame_isoch_oneway_transit(stats, packet); + } else if (isPeriodicBurst(stats->common) && packet->frameID) { + reporter_handle_txmsg_oneway_transit(stats, packet); + } + } } } @@ -898,50 +948,15 @@ static inline void reporter_handle_packet_tcpistats (struct ReporterData *data, struct ReportStruct *packet) { assert(data!=NULL); struct TransferInfo *stats = &data->info; - stats->sock_callstats.write.TCPretry += (packet->tcpstats.retry_tot - stats->sock_callstats.write.totTCPretry); - stats->sock_callstats.write.totTCPretry = packet->tcpstats.retry_tot; - stats->sock_callstats.write.cwnd = packet->tcpstats.cwnd; - stats->sock_callstats.write.rtt = packet->tcpstats.rtt; - stats->sock_callstats.write.rttvar = packet->tcpstats.rttvar; + stats->sock_callstats.write.tcpstats.retry += (packet->tcpstats.retry_tot - stats->sock_callstats.write.tcpstats.retry_prev); + stats->sock_callstats.write.tcpstats.retry_prev = packet->tcpstats.retry_tot; + stats->sock_callstats.write.tcpstats.retry_tot = packet->tcpstats.retry_tot; + stats->sock_callstats.write.tcpstats.cwnd = packet->tcpstats.cwnd; + stats->sock_callstats.write.tcpstats.rtt = packet->tcpstats.rtt; + stats->sock_callstats.write.tcpstats.rttvar = packet->tcpstats.rttvar; } #endif -void reporter_handle_packet_client (struct ReporterData *data, struct ReportStruct *packet) { - struct TransferInfo *stats = &data->info; - stats->ts.packetTime = packet->packetTime; - if (!packet->emptyreport) { - stats->total.Bytes.current += packet->packetLen; - if (packet->errwrite && (packet->errwrite != WriteErrNoAccount)) { - stats->sock_callstats.write.WriteErr++; - stats->sock_callstats.write.totWriteErr++; - } - // These are valid packets that need standard iperf accounting - stats->sock_callstats.write.WriteCnt += packet->writecnt; - stats->sock_callstats.write.totWriteCnt += packet->writecnt; - if (isIsochronous(stats->common)) { - reporter_handle_packet_isochronous(data, packet); - } else if (isPeriodicBurst(stats->common)) { - reporter_handle_burst_tcp_client_transit(data, packet); - } -#if HAVE_DECL_TCP_NOTSENT_LOWAT - if (stats->latency_histogram && (packet->select_delay > 0.0)) { - histogram_insert(stats->latency_histogram, packet->select_delay, &packet->packetTime); - } - if (isTcpDrain(stats->common) && packet->transit_ready && (packet->drain_time)) { - reporter_mmm_update(&stats->drain_mmm.current, (double) packet->drain_time); - reporter_mmm_update(&stats->drain_mmm.total, (double) packet->drain_time); - if (stats->drain_histogram ) { - // convert drain time from microseconds to seconds prior to insert - histogram_insert(stats->drain_histogram, (1e-6 * packet->drain_time), &packet->packetTime); - } - } -#endif - } - if (isUDP(stats->common)) { - stats->PacketID = packet->packetID; - reporter_handle_packet_pps(data, packet); - } -} /* * Report printing routines below @@ -968,7 +983,7 @@ times->iStart = times->iEnd; times->iEnd = TimeDifference(times->packetTime, times->startTime); break; - case FRAME: + case INTERVALPARTIAL: if ((times->iStart = TimeDifference(times->prevpacketTime, times->startTime)) < 0) times->iStart = 0.0; times->iEnd = TimeDifference(times->packetTime, times->startTime); @@ -1004,19 +1019,24 @@ stats->isochstats.framelostcnt.prev = stats->isochstats.framelostcnt.current; stats->isochstats.slipcnt.prev = stats->isochstats.slipcnt.current; #if HAVE_TCP_STATS - stats->sock_callstats.write.TCPretry = 0; + // set the interval retry counter to zero + stats->sock_callstats.write.tcpstats.retry = 0; #endif -#if HAVE_DECL_TCP_NOTSENT_LOWAT - if (isTcpDrain(stats->common)) { - stats->drain_mmm.current.cnt = 0; - stats->drain_mmm.current.min = FLT_MAX; - stats->drain_mmm.current.max = FLT_MIN; - stats->drain_mmm.current.sum = 0; - stats->drain_mmm.current.vd = 0; - stats->drain_mmm.current.mean = 0; - stats->drain_mmm.current.m2 = 0; + if (isBounceBack(stats->common)) { + reporter_reset_mmm(&stats->bbrtt.current); + reporter_reset_mmm(&stats->bbowdto.current); + reporter_reset_mmm(&stats->bbowdfro.current); + reporter_reset_mmm(&stats->bbasym.current); + } + if (isTcpWriteTimes(stats->common)) { + stats->write_mmm.current.cnt = 0; + stats->write_mmm.current.min = FLT_MAX; + stats->write_mmm.current.max = FLT_MIN; + stats->write_mmm.current.sum = 0; + stats->write_mmm.current.vd = 0; + stats->write_mmm.current.mean = 0; + stats->write_mmm.current.m2 = 0; } -#endif } static inline void reporter_reset_transfer_stats_client_udp (struct TransferInfo *stats) { @@ -1043,13 +1063,7 @@ for (ix = 0; ix < 8; ix++) { stats->sock_callstats.read.bins[ix] = 0; } - stats->transit.minTransit = FLT_MAX; - stats->transit.maxTransit = FLT_MIN; - stats->transit.sumTransit = 0; - stats->transit.cntTransit = 0; - stats->transit.vdTransit = 0; - stats->transit.meanTransit = 0; - stats->transit.m2Transit = 0; + reporter_reset_mmm(&stats->transit.current); stats->IPGsum = 0; } @@ -1060,13 +1074,7 @@ stats->total.OutofOrder.prev = stats->total.OutofOrder.current; stats->total.Lost.prev = stats->total.Lost.current; stats->total.IPG.prev = stats->total.IPG.current; - stats->transit.minTransit = FLT_MAX; - stats->transit.maxTransit = FLT_MIN; - stats->transit.sumTransit = 0; - stats->transit.cntTransit = 0; - stats->transit.vdTransit = 0; - stats->transit.meanTransit = 0; - stats->transit.m2Transit = 0; + reporter_reset_mmm(&stats->transit.current); stats->isochstats.framecnt.prev = stats->isochstats.framecnt.current; stats->isochstats.framelostcnt.prev = stats->isochstats.framelostcnt.current; stats->isochstats.slipcnt.prev = stats->isochstats.slipcnt.current; @@ -1165,13 +1173,7 @@ stats->l2counts.unknown = stats->l2counts.tot_unknown; stats->l2counts.udpcsumerr = stats->l2counts.tot_udpcsumerr; stats->l2counts.lengtherr = stats->l2counts.tot_lengtherr; - stats->transit.minTransit = stats->transit.totminTransit; - stats->transit.maxTransit = stats->transit.totmaxTransit; - stats->transit.cntTransit = stats->transit.totcntTransit; - stats->transit.sumTransit = stats->transit.totsumTransit; - stats->transit.meanTransit = stats->transit.totmeanTransit; - stats->transit.m2Transit = stats->transit.totm2Transit; - stats->transit.vdTransit = stats->transit.totvdTransit; + stats->transit.current = stats->transit.total; if (isIsochronous(stats->common)) { stats->isochstats.cntFrames = stats->isochstats.framecnt.current; stats->isochstats.cntFramesMissed = stats->isochstats.framelostcnt.current; @@ -1365,11 +1367,7 @@ stats->isochstats.cntFramesMissed = stats->isochstats.framelostcnt.current; stats->isochstats.cntSlips = stats->isochstats.slipcnt.current; } - stats->transit.sumTransit = stats->transit.totsumTransit; - stats->transit.cntTransit = stats->transit.totcntTransit; - stats->transit.minTransit = stats->transit.totminTransit; - stats->transit.maxTransit = stats->transit.totmaxTransit; - stats->transit.m2Transit = stats->transit.totm2Transit; + stats->transit.current = stats->transit.total; if (stats->framelatency_histogram) { stats->framelatency_histogram->final = 1; } @@ -1393,14 +1391,12 @@ struct TransferInfo *sumstats = (data->GroupSumReport != NULL) ? &data->GroupSumReport->info : NULL; struct TransferInfo *fullduplexstats = (data->FullDuplexReport != NULL) ? &data->FullDuplexReport->info : NULL; stats->cntBytes = stats->total.Bytes.current - stats->total.Bytes.prev; -#if HAVE_DECL_TCP_NOTSENT_LOWAT if (stats->latency_histogram) { stats->latency_histogram->final = final; } - if (stats->drain_histogram) { - stats->drain_histogram->final = final; + if (stats->write_histogram) { + stats->write_histogram->final = final; } -#endif if (isIsochronous(stats->common)) { if (final) { stats->isochstats.cntFrames = stats->isochstats.framecnt.current; @@ -1420,22 +1416,20 @@ sumstats->sock_callstats.write.totWriteCnt += stats->sock_callstats.write.WriteCnt; sumstats->threadcnt++; #if HAVE_TCP_STATS - sumstats->sock_callstats.write.TCPretry += stats->sock_callstats.write.TCPretry; - sumstats->sock_callstats.write.totTCPretry += stats->sock_callstats.write.TCPretry; + sumstats->sock_callstats.write.tcpstats.retry += stats->sock_callstats.write.tcpstats.retry; + sumstats->sock_callstats.write.tcpstats.retry_tot += stats->sock_callstats.write.tcpstats.retry; #endif } if (fullduplexstats) { fullduplexstats->total.Bytes.current += stats->cntBytes; } if (final) { -#if HAVE_DECL_TCP_NOTSENT_LOWAT if (stats->latency_histogram) { stats->latency_histogram->final = 1; } - if (stats->drain_histogram) { - stats->drain_histogram->final = 1; + if (stats->write_histogram) { + stats->write_histogram->final = 1; } -#endif if ((stats->cntBytes > 0) && stats->output_handler && !TimeZero(stats->ts.intervalTime)) { // print a partial interval report if enable and this a final if ((stats->output_handler) && !(stats->isMaskOutput)) { @@ -1458,15 +1452,13 @@ stats->sock_callstats.write.WriteErr = stats->sock_callstats.write.totWriteErr; stats->sock_callstats.write.WriteCnt = stats->sock_callstats.write.totWriteCnt; #if HAVE_TCP_STATS - stats->sock_callstats.write.TCPretry = stats->sock_callstats.write.totTCPretry; + stats->sock_callstats.write.tcpstats.retry = stats->sock_callstats.write.tcpstats.retry_tot; #endif if (stats->framelatency_histogram) { stats->framelatency_histogram->final = 1; } stats->cntBytes = stats->total.Bytes.current; -#if HAVE_DECL_TCP_NOTSENT_LOWAT - stats->drain_mmm.current = stats->drain_mmm.total; -#endif + stats->write_mmm.current = stats->write_mmm.total; reporter_set_timestamps_time(&stats->ts, TOTAL); } else if (isIsochronous(stats->common)) { stats->isochstats.cntFrames = stats->isochstats.framecnt.current - stats->isochstats.framecnt.prev; @@ -1503,7 +1495,7 @@ stats->sock_callstats.write.WriteErr = stats->sock_callstats.write.totWriteErr; stats->sock_callstats.write.WriteCnt = stats->sock_callstats.write.totWriteCnt; #if HAVE_TCP_STATS - stats->sock_callstats.write.TCPretry = stats->sock_callstats.write.totTCPretry; + stats->sock_callstats.write.tcpstats.retry = stats->sock_callstats.write.tcpstats.retry_tot; #endif stats->cntBytes = stats->total.Bytes.current; reporter_set_timestamps_time(&stats->ts, TOTAL); @@ -1512,6 +1504,62 @@ } } +void reporter_transfer_protocol_client_bb_tcp (struct ReporterData *data, int final) { + struct TransferInfo *stats = &data->info; + if (final) { + if ((stats->cntBytes > 0) && stats->output_handler && !TimeZero(stats->ts.intervalTime)) { + // print a partial interval report if enable and this a final + if ((stats->output_handler) && !(stats->isMaskOutput)) { + reporter_set_timestamps_time(&stats->ts, FINALPARTIAL); + if ((stats->ts.iEnd - stats->ts.iStart) > stats->ts.significant_partial) + (*stats->output_handler)(stats); + reporter_reset_transfer_stats_client_tcp(stats); + } + } +#if HAVE_TCP_STATS + stats->sock_callstats.write.tcpstats.retry = stats->sock_callstats.write.tcpstats.retry_tot; +#endif + stats->cntBytes = stats->total.Bytes.current; + reporter_set_timestamps_time(&stats->ts, TOTAL); + stats->final=1; + } else { + stats->final=0; + stats->cntBytes = stats->total.Bytes.current - stats->total.Bytes.prev; + } + if ((stats->output_handler) && !(stats->isMaskOutput)) + (*stats->output_handler)(stats); + if (!final) + reporter_reset_transfer_stats_client_tcp(stats); +} + +void reporter_transfer_protocol_server_bb_tcp (struct ReporterData *data, int final) { + struct TransferInfo *stats = &data->info; + if (final) { + if ((stats->cntBytes > 0) && stats->output_handler && !TimeZero(stats->ts.intervalTime)) { + // print a partial interval report if enable and this a final + if ((stats->output_handler) && !(stats->isMaskOutput)) { + reporter_set_timestamps_time(&stats->ts, FINALPARTIAL); + if ((stats->ts.iEnd - stats->ts.iStart) > stats->ts.significant_partial) + (*stats->output_handler)(stats); + reporter_reset_transfer_stats_server_tcp(stats); + } + } +#if HAVE_TCP_STATS + stats->sock_callstats.write.tcpstats.retry = stats->sock_callstats.write.tcpstats.retry_tot; +#endif + stats->cntBytes = stats->total.Bytes.current; + reporter_set_timestamps_time(&stats->ts, TOTAL); + stats->final=1; + } else { + stats->final=0; + stats->cntBytes = stats->total.Bytes.current - stats->total.Bytes.prev; + } + if ((stats->output_handler) && !(stats->isMaskOutput)) + (*stats->output_handler)(stats); + if (!final) + reporter_reset_transfer_stats_client_tcp(stats); +} + void reporter_transfer_protocol_sum_server_tcp (struct TransferInfo *stats, int final) { if (!final || (final && (stats->cntBytes > 0) && !TimeZero(stats->ts.intervalTime))) { stats->cntBytes = stats->total.Bytes.current - stats->total.Bytes.prev; @@ -1643,10 +1691,9 @@ // Conditional print based on bursts or frames int reporter_condprint_frame_interval_report_server_udp (struct ReporterData *data, struct ReportStruct *packet) { - int advance_jobq = 0; struct TransferInfo *stats = &data->info; + int advance_jobq = 0; // first packet of a burst and not a duplicate - assert(packet->burstsize != 0); if ((packet->burstsize == (packet->remaining + packet->packetLen)) && (stats->matchframeID != packet->frameID)) { stats->matchframeID=packet->frameID; } @@ -1678,25 +1725,36 @@ } int reporter_condprint_burst_interval_report_server_tcp (struct ReporterData *data, struct ReportStruct *packet) { - assert(packet->burstsize != 0); struct TransferInfo *stats = &data->info; + int advance_jobq = 0; + if (packet->transit_ready) { + stats->ts.prevpacketTime = packet->prevSentTime; + stats->ts.packetTime = packet->packetTime; + reporter_set_timestamps_time(&stats->ts, INTERVALPARTIAL); + stats->cntBytes = stats->total.Bytes.current - stats->total.Bytes.prev; + if ((stats->output_handler) && !(stats->isMaskOutput)) + (*stats->output_handler)(stats); + reporter_reset_transfer_stats_server_tcp(stats); + advance_jobq = 1; + } + return advance_jobq; +} +int reporter_condprint_burst_interval_report_client_tcp (struct ReporterData *data, struct ReportStruct *packet) { + + struct TransferInfo *stats = &data->info; int advance_jobq = 0; // first packet of a burst and not a duplicate if (packet->transit_ready) { - stats->tripTime = reporter_handle_packet_oneway_transit(data, packet); - if (stats->framelatency_histogram) { - histogram_insert(stats->framelatency_histogram, stats->tripTime, &packet->sentTime); - } - stats->tripTime *= 1e3; // convert from secs millisecs + reporter_handle_packet_oneway_transit(stats, packet); // printf("****sndpkt=%ld.%ld rxpkt=%ld.%ld\n", packet->sentTime.tv_sec, packet->sentTime.tv_usec, packet->packetTime.tv_sec,packet->packetTime.tv_usec); stats->ts.prevpacketTime = packet->prevSentTime; stats->ts.packetTime = packet->packetTime; - reporter_set_timestamps_time(&stats->ts, FRAME); + reporter_set_timestamps_time(&stats->ts, INTERVALPARTIAL); stats->cntBytes = stats->total.Bytes.current - stats->total.Bytes.prev; if ((stats->output_handler) && !(stats->isMaskOutput)) (*stats->output_handler)(stats); - reporter_reset_transfer_stats_server_tcp(stats); + reporter_reset_transfer_stats_client_tcp(stats); advance_jobq = 1; } return advance_jobq; diff -Nru iperf-2.1.5+dfsg1/src/ReportOutputs.c iperf-2.1.7+dfsg1/src/ReportOutputs.c --- iperf-2.1.5+dfsg1/src/ReportOutputs.c 2021-12-05 20:49:56.000000000 +0000 +++ iperf-2.1.7+dfsg1/src/ReportOutputs.c 2022-04-11 18:13:03.000000000 +0000 @@ -57,16 +57,17 @@ #define SNBUFFEREXTENDSIZE 512 static char outbuffer[SNBUFFERSIZE]; // Buffer for printing static char outbufferext[SNBUFFEREXTENDSIZE]; // Buffer for printing -static char outbufferext2[SNBUFFEREXTENDSIZE]; // Buffer for printing + static char llaw_buf[100]; static char netpower_buf[100]; static int HEADING_FLAG(report_bw) = 0; +static int HEADING_FLAG(report_client_bb_bw) = 0; static int HEADING_FLAG(report_bw_jitter_loss) = 0; static int HEADING_FLAG(report_bw_read_enhanced) = 0; static int HEADING_FLAG(report_bw_read_enhanced_netpwr) = 0; static int HEADING_FLAG(report_bw_write_enhanced) = 0; -static int HEADING_FLAG(report_write_enhanced_drain) = 0; +static int HEADING_FLAG(report_write_enhanced_write) = 0; static int HEADING_FLAG(report_bw_write_enhanced_netpwr) = 0; static int HEADING_FLAG(report_bw_pps_enhanced) = 0; static int HEADING_FLAG(report_bw_pps_enhanced_isoch) = 0; @@ -87,16 +88,18 @@ static int HEADING_FLAG(report_bw_jitter_loss_enhanced_isoch_triptime) = 0; static int HEADING_FLAG(report_sumcnt_bw_jitter_loss) = 0; static int HEADING_FLAG(report_burst_read_tcp) = 0; +static int HEADING_FLAG(report_burst_write_tcp) = 0; void reporter_default_heading_flags (int flag) { HEADING_FLAG(report_bw) = flag; + HEADING_FLAG(report_client_bb_bw) = flag; HEADING_FLAG(report_sumcnt_bw) = flag; HEADING_FLAG(report_sumcnt_udp_fullduplex) = flag; HEADING_FLAG(report_bw_jitter_loss) = flag; HEADING_FLAG(report_bw_read_enhanced) = flag; HEADING_FLAG(report_bw_read_enhanced_netpwr) = flag; HEADING_FLAG(report_bw_write_enhanced) = flag; - HEADING_FLAG(report_write_enhanced_drain) = flag; + HEADING_FLAG(report_write_enhanced_write) = flag; HEADING_FLAG(report_write_enhanced_isoch) = flag; HEADING_FLAG(report_bw_write_enhanced_netpwr) = flag; HEADING_FLAG(report_bw_pps_enhanced) = flag; @@ -113,6 +116,7 @@ HEADING_FLAG(report_sumcnt_bw_jitter_loss) = flag; HEADING_FLAG(report_sumcnt_bw_pps_enhanced) = flag; HEADING_FLAG(report_burst_read_tcp) = flag; + HEADING_FLAG(report_burst_write_tcp) = flag; } static inline void _print_stats_common (struct TransferInfo *stats) { assert(stats!=NULL); @@ -177,8 +181,12 @@ double netpwr = NETPOWERCONSTANT * (((double) stats->cntBytes) / (stats->ts.iEnd - stats->ts.iStart) / meantransit); if (netpwr < NETPWR_LOWERBOUNDS) { strcpy(netpower_buf, "OBL"); - } else { + } else if (netpwr > 100) { snprintf(netpower_buf, sizeof(netpower_buf), "%.0f", netpwr); + } else if (netpwr > 10) { + snprintf(netpower_buf, sizeof(netpower_buf), "%.2f", netpwr); + } else { + snprintf(netpower_buf, sizeof(netpower_buf), "%.6f", netpwr); } } } @@ -231,7 +239,7 @@ } void tcp_output_read_enhanced_triptime (struct TransferInfo *stats) { HEADING_PRINT_COND(report_bw_read_enhanced_netpwr); - double meantransit = (stats->transit.cntTransit > 0) ? (stats->transit.sumTransit / stats->transit.cntTransit) : 0; + double meantransit = (stats->transit.current.cnt > 0) ? (stats->transit.current.sum / stats->transit.current.cnt) : 0; double lambda = (stats->IPGsum > 0.0) ? ((double)stats->cntBytes / stats->IPGsum) : 0.0; set_llawbuf(lambda, meantransit, stats); _print_stats_common(stats); @@ -241,11 +249,11 @@ stats->common->transferIDStr, stats->ts.iStart, stats->ts.iEnd, outbuffer, outbufferext, (meantransit * 1e3), - (stats->transit.cntTransit < 2) ? 0 : stats->transit.minTransit*1e3, - (stats->transit.cntTransit < 2) ? 0 : stats->transit.maxTransit*1e3, - (stats->transit.cntTransit < 2) ? 0 : sqrt(stats->transit.m2Transit / (stats->transit.cntTransit - 1)) / 1e3, - stats->transit.cntTransit, - stats->transit.cntTransit ? (long) ((double)stats->cntBytes / (double) stats->transit.cntTransit) : 0, + (stats->transit.current.cnt < 2) ? 0 : stats->transit.current.min * 1e3, + (stats->transit.current.cnt < 2) ? 0 : stats->transit.current.max * 1e3, + (stats->transit.current.cnt < 2) ? 0 : 1e3 * (sqrt(stats->transit.current.m2 / (stats->transit.current.cnt - 1))), + stats->transit.current.cnt, + stats->transit.current.cnt ? (long) ((double)stats->cntBytes / (double) stats->transit.current.cnt) : 0, llaw_buf, netpower_buf, stats->sock_callstats.read.cntRead, @@ -263,11 +271,11 @@ stats->common->transferIDStr, stats->ts.iStart, stats->ts.iEnd, outbuffer, outbufferext, (meantransit * 1e3), - stats->transit.minTransit*1e3, - stats->transit.maxTransit*1e3, - (stats->transit.cntTransit < 2) ? 0 : sqrt(stats->transit.m2Transit / (stats->transit.cntTransit - 1)) / 1e3, - stats->transit.cntTransit, - stats->transit.cntTransit ? (long) ((double)stats->cntBytes / (double) stats->transit.cntTransit) : 0, + stats->transit.current.min * 1e3, + stats->transit.current.max * 1e3, + (stats->transit.current.cnt < 2) ? 0 : 1e3 * (sqrt(stats->transit.current.m2 / (stats->transit.current.cnt - 1))), + stats->transit.current.cnt, + stats->transit.current.cnt ? (long) ((double)stats->cntBytes / (double) stats->transit.current.cnt) : 0, llaw_buf, netpower_buf, stats->sock_callstats.read.cntRead, @@ -309,11 +317,12 @@ HEADING_PRINT_COND(report_burst_read_tcp); _print_stats_common(stats); if (!stats->final) { - set_netpowerbuf(stats->tripTime, stats); + set_netpowerbuf(stats->transit.current.mean, stats); printf(report_burst_read_tcp_format, stats->common->transferIDStr, stats->ts.iStart, stats->ts.iEnd, outbuffer, outbufferext, - stats->tripTime, + stats->transit.current.mean * 1e3, + (1e2 * stats->transit.current.mean * stats->common->FPS), // (1e3 / 100%) stats->sock_callstats.read.cntRead, stats->sock_callstats.read.bins[0], stats->sock_callstats.read.bins[1], @@ -323,12 +332,15 @@ stats->sock_callstats.read.bins[5], stats->sock_callstats.read.bins[6], stats->sock_callstats.read.bins[7], - (stats->tripTime * stats->common->FPS) / 10.0, // (1e3 / 100%) netpower_buf); } else { printf(report_burst_read_tcp_final_format, stats->common->transferIDStr, stats->ts.iStart, stats->ts.iEnd, outbuffer, outbufferext, + stats->transit.total.mean * 1e3, + (stats->transit.total.cnt < 2) ? 0 : stats->transit.total.min * 1e3, + (stats->transit.total.cnt < 2) ? 0 : stats->transit.total.max * 1e3, + (stats->transit.total.cnt < 2) ? 0 : 1e3 * (sqrt(stats->transit.total.m2 / (stats->transit.total.cnt - 1))), stats->sock_callstats.read.cntRead, stats->sock_callstats.read.bins[0], stats->sock_callstats.read.bins[1], @@ -352,6 +364,112 @@ fflush(stdout); } +void tcp_output_write_bb (struct TransferInfo *stats) { + HEADING_PRINT_COND(report_client_bb_bw); + _print_stats_common(stats); + if (stats->final) { + int cnt_sec = (stats->bbrtt.total.cnt > 0) ? ((int) ((double) stats->bbrtt.total.cnt / (stats->ts.iEnd - stats->ts.iStart))) : 0; +#if HAVE_TCP_STATS + printf(report_client_bb_bw_format, stats->common->transferIDStr, + stats->ts.iStart, stats->ts.iEnd, + outbuffer, outbufferext, + stats->bbrtt.total.cnt, + cnt_sec, + (stats->bbrtt.total.mean * 1e3), + (stats->bbrtt.total.cnt < 2) ? 0 : (stats->bbrtt.total.min * 1e3), + (stats->bbrtt.total.cnt < 2) ? 0 : (stats->bbrtt.total.max * 1e3), + (stats->bbrtt.total.cnt < 2) ? 0 : 1e3 * (sqrt(stats->bbrtt.total.m2 / (stats->bbrtt.total.cnt - 1))), + stats->sock_callstats.write.tcpstats.retry, + stats->sock_callstats.write.tcpstats.cwnd, + stats->sock_callstats.write.tcpstats.rtt); +#else + printf(report_client_bb_bw_format, stats->common->transferIDStr, + stats->ts.iStart, stats->ts.iEnd, + outbuffer, outbufferext, + stats->bbrtt.total.cnt, + cnt_sec, + (stats->bbrtt.total.mean * 1e3), + (stats->bbrtt.total.cnt < 2) ? 0 : (stats->bbrtt.total.min * 1e3), + (stats->bbrtt.total.cnt < 2) ? 0 : (stats->bbrtt.total.max * 1e3), + (stats->bbrtt.total.cnt < 2) ? 0 : 1e3 * (sqrt(stats->bbrtt.total.m2 / (stats->bbrtt.total.cnt - 1)))); +#endif + if (isTripTime(stats->common)) { + printf(report_client_bb_bw_triptime_format, stats->common->transferIDStr, + stats->ts.iStart, stats->ts.iEnd, + stats->bbowdto.total.cnt, + cnt_sec, + (stats->bbowdto.total.mean * 1e3), + (stats->bbowdto.total.cnt < 2) ? 0 : (stats->bbowdto.total.min * 1e3), + (stats->bbowdto.total.cnt < 2) ? 0 : (stats->bbowdto.total.max * 1e3), + (stats->bbowdto.total.cnt < 2) ? 0 : 1e3 * (sqrt(stats->bbowdto.total.m2 / (stats->bbowdto.total.cnt - 1))), + (stats->bbowdfro.total.mean * 1e3), + (stats->bbowdfro.total.cnt < 2) ? 0 : (stats->bbowdfro.total.min * 1e3), + (stats->bbowdfro.total.cnt < 2) ? 0 : (stats->bbowdfro.total.max * 1e3), + (stats->bbowdfro.total.cnt < 2) ? 0 : 1e3 * (sqrt(stats->bbowdfro.total.m2 / (stats->bbowdfro.total.cnt - 1))), + (stats->bbasym.total.mean * 1e3), + (stats->bbasym.total.cnt < 2) ? 0 : (stats->bbasym.total.min * 1e3), + (stats->bbasym.total.cnt < 2) ? 0 : (stats->bbasym.total.max * 1e3), + (stats->bbasym.total.cnt < 2) ? 0 : 1e3 * (sqrt(stats->bbasym.total.m2 / (stats->bbasym.total.cnt - 1)))); + } + if (stats->bbrtt_histogram) { + histogram_print(stats->bbrtt_histogram, stats->ts.iStart, stats->ts.iEnd); + } + } else { + int cnt_sec = (stats->bbrtt.total.cnt > 0) ? ((int) ((double) stats->bbrtt.current.cnt / (stats->ts.iEnd - stats->ts.iStart))) : 0; +#if HAVE_TCP_STATS + printf(report_client_bb_bw_format, stats->common->transferIDStr, + stats->ts.iStart, stats->ts.iEnd, + outbuffer, outbufferext, + stats->bbrtt.current.cnt, + cnt_sec, + (stats->bbrtt.current.mean * 1e3), + (stats->bbrtt.current.cnt < 2) ? 0 : (stats->bbrtt.current.min * 1e3), + (stats->bbrtt.current.cnt < 2) ? 0 : (stats->bbrtt.current.max * 1e3), + (stats->bbrtt.current.cnt < 2) ? 0 : 1e3 * (sqrt(stats->bbrtt.current.m2 / (stats->bbrtt.current.cnt - 1))), + stats->sock_callstats.write.tcpstats.retry, + stats->sock_callstats.write.tcpstats.cwnd, + stats->sock_callstats.write.tcpstats.rtt); +#else + printf(report_client_bb_bw_format, stats->common->transferIDStr, + stats->ts.iStart, stats->ts.iEnd, + outbuffer, outbufferext, + stats->bbrtt.current.cnt, + cnt_sec, + (stats->bbrtt.current.mean * 1e3), + (stats->bbrtt.current.cnt < 2) ? 0 : (stats->bbrtt.current.min * 1e3), + (stats->bbrtt.current.cnt < 2) ? 0 : (stats->bbrtt.current.max * 1e3), + (stats->bbrtt.current.cnt < 2) ? 0 : 1e3 * (sqrt(stats->bbrtt.current.m2 / (stats->bbrtt.current.cnt - 1)))); +#endif + } + fflush(stdout); +} + +void tcp_output_burst_write (struct TransferInfo *stats) { + HEADING_PRINT_COND(report_burst_write_tcp); + _print_stats_common(stats); +#if HAVE_TCP_STATS + set_netpowerbuf((stats->transit.current.mean + stats->sock_callstats.write.tcpstats.rtt), stats); + printf(report_burst_write_tcp_format, stats->common->transferIDStr, + stats->ts.iStart, stats->ts.iEnd, + outbuffer, outbufferext, + stats->transit.current.mean, + stats->sock_callstats.write.WriteCnt, + stats->sock_callstats.write.WriteErr, + stats->sock_callstats.write.tcpstats.retry, + stats->sock_callstats.write.tcpstats.cwnd, + stats->sock_callstats.write.tcpstats.rtt, + netpower_buf); + #else + printf(report_burst_write_tcp_format, stats->common->transferIDStr, + stats->ts.iStart, stats->ts.iEnd, + outbuffer, outbufferext, + stats->transit.current.mean, + stats->sock_callstats.write.WriteCnt, + stats->sock_callstats.write.WriteErr); +#endif + fflush(stdout); +} + void tcp_output_write_enhanced (struct TransferInfo *stats) { HEADING_PRINT_COND(report_bw_write_enhanced); _print_stats_common(stats); @@ -362,17 +480,17 @@ stats->sock_callstats.write.WriteCnt, stats->sock_callstats.write.WriteErr); #else - set_netpowerbuf(stats->sock_callstats.write.rtt * 1e-6, stats); - if (stats->sock_callstats.write.cwnd > 0) { + set_netpowerbuf(stats->sock_callstats.write.tcpstats.rtt * 1e-6, stats); + if (stats->sock_callstats.write.tcpstats.cwnd > 0) { printf(report_bw_write_enhanced_format, stats->common->transferIDStr, stats->ts.iStart, stats->ts.iEnd, outbuffer, outbufferext, stats->sock_callstats.write.WriteCnt, stats->sock_callstats.write.WriteErr, - stats->sock_callstats.write.TCPretry, - stats->sock_callstats.write.cwnd, - stats->sock_callstats.write.rtt, - stats->sock_callstats.write.rttvar, + stats->sock_callstats.write.tcpstats.retry, + stats->sock_callstats.write.tcpstats.cwnd, + stats->sock_callstats.write.tcpstats.rtt, + stats->sock_callstats.write.tcpstats.rttvar, netpower_buf); } else { printf(report_bw_write_enhanced_nocwnd_format, @@ -380,8 +498,8 @@ outbuffer, outbufferext, stats->sock_callstats.write.WriteCnt, stats->sock_callstats.write.WriteErr, - stats->sock_callstats.write.TCPretry, - stats->sock_callstats.write.rtt, + stats->sock_callstats.write.tcpstats.retry, + stats->sock_callstats.write.tcpstats.rtt, netpower_buf); } #endif @@ -393,66 +511,64 @@ fflush(stdout); } -#if HAVE_DECL_TCP_NOTSENT_LOWAT -void tcp_output_write_enhanced_drain (struct TransferInfo *stats) { - HEADING_PRINT_COND(report_write_enhanced_drain); +void tcp_output_write_enhanced_write (struct TransferInfo *stats) { + HEADING_PRINT_COND(report_write_enhanced_write); _print_stats_common(stats); #if !(HAVE_TCP_STATS) - printf(report_write_enhanced_drain_format, + printf(report_write_enhanced_write_format, stats->common->transferIDStr, stats->ts.iStart, stats->ts.iEnd, outbuffer, outbufferext, stats->sock_callstats.write.WriteCnt, stats->sock_callstats.write.WriteErr, - - stats->drain_mmm.current.mean * 1e-3, - stats->drain_mmm.current.min * 1e-3, - stats->drain_mmm.current.max * 1e-3, - (stats->drain_mmm.current.cnt < 2) ? 0 : (1e-3 * sqrt(stats->drain_mmm.current.m2 / (stats->drain_mmm.current.cnt - 1))), - stats->drain_mmm.current.cnt); + stats->write_mmm.current.mean * 1e3, + stats->write_mmm.current.min * 1e3, + stats->write_mmm.current.max * 1e3, + (stats->write_mmm.current.cnt < 2) ? 0 : (1e-3 * sqrt(stats->write_mmm.current.m2 / (stats->write_mmm.current.cnt - 1))), + stats->write_mmm.current.cnt); #else - set_netpowerbuf(stats->sock_callstats.write.rtt * 1e-6, stats); - if (stats->sock_callstats.write.cwnd > 0) { - printf(report_write_enhanced_drain_format, + set_netpowerbuf(stats->sock_callstats.write.tcpstats.rtt * 1e-6, stats); + if (stats->sock_callstats.write.tcpstats.cwnd > 0) { + printf(report_write_enhanced_write_format, stats->common->transferIDStr, stats->ts.iStart, stats->ts.iEnd, outbuffer, outbufferext, stats->sock_callstats.write.WriteCnt, stats->sock_callstats.write.WriteErr, - stats->sock_callstats.write.TCPretry, - stats->sock_callstats.write.cwnd, - stats->sock_callstats.write.rtt, + stats->sock_callstats.write.tcpstats.retry, + stats->sock_callstats.write.tcpstats.cwnd, + stats->sock_callstats.write.tcpstats.rtt, netpower_buf, - stats->drain_mmm.current.cnt, - stats->drain_mmm.current.mean * 1e-3, - stats->drain_mmm.current.min * 1e-3, - stats->drain_mmm.current.max * 1e-3, - (stats->drain_mmm.current.cnt < 2) ? 0 : (1e-3 * sqrt(stats->drain_mmm.current.m2 / (stats->drain_mmm.current.cnt - 1))), - stats->drain_mmm.current.cnt); + stats->write_mmm.current.cnt, + stats->write_mmm.current.mean * 1e-3, + stats->write_mmm.current.min * 1e-3, + stats->write_mmm.current.max * 1e-3, + (stats->write_mmm.current.cnt < 2) ? 0 : (1e-3 * sqrt(stats->write_mmm.current.m2 / (stats->write_mmm.current.cnt - 1))), + stats->write_mmm.current.cnt); } else { - printf(report_write_enhanced_nocwnd_drain_format, + printf(report_write_enhanced_nocwnd_write_format, stats->common->transferIDStr, stats->ts.iStart, stats->ts.iEnd, outbuffer, outbufferext, stats->sock_callstats.write.WriteCnt, stats->sock_callstats.write.WriteErr, - stats->sock_callstats.write.TCPretry, - stats->sock_callstats.write.rtt, + stats->sock_callstats.write.tcpstats.retry, + stats->sock_callstats.write.tcpstats.rtt, netpower_buf, - stats->drain_mmm.current.cnt, - stats->drain_mmm.current.mean * 1e-3, - stats->drain_mmm.current.min * 1e-3, - stats->drain_mmm.current.max * 1e-3, - (stats->drain_mmm.current.cnt < 2) ? 0 : (1e-3 * sqrt(stats->drain_mmm.current.m2 / (stats->drain_mmm.current.cnt - 1))), - stats->drain_mmm.current.cnt); + stats->write_mmm.current.cnt, + stats->write_mmm.current.mean * 1e3, + stats->write_mmm.current.min * 1e3, + stats->write_mmm.current.max * 1e3, + (stats->write_mmm.current.cnt < 2) ? 0 : (1e3 * sqrt(stats->write_mmm.current.m2 / (stats->write_mmm.current.cnt - 1))), + stats->write_mmm.current.cnt); } #endif if (stats->latency_histogram) { histogram_print(stats->latency_histogram, stats->ts.iStart, stats->ts.iEnd); } - if (stats->drain_histogram) { - histogram_print(stats->drain_histogram, stats->ts.iStart, stats->ts.iEnd); + if (stats->write_histogram) { + histogram_print(stats->write_histogram, stats->ts.iStart, stats->ts.iEnd); } fflush(stdout); } -#endif + void tcp_output_write_enhanced_isoch (struct TransferInfo *stats) { HEADING_PRINT_COND(report_write_enhanced_isoch); _print_stats_common(stats); @@ -464,16 +580,16 @@ stats->sock_callstats.write.WriteErr, stats->isochstats.cntFrames, stats->isochstats.cntFramesMissed, stats->isochstats.cntSlips); #else - set_netpowerbuf(stats->sock_callstats.write.rtt * 1e-6, stats); - if (stats->sock_callstats.write.cwnd > 0) { + set_netpowerbuf(stats->sock_callstats.write.tcpstats.rtt * 1e-6, stats); + if (stats->sock_callstats.write.tcpstats.cwnd > 0) { printf(report_write_enhanced_isoch_format, stats->common->transferIDStr, stats->ts.iStart, stats->ts.iEnd, outbuffer, outbufferext, stats->sock_callstats.write.WriteCnt, stats->sock_callstats.write.WriteErr, - stats->sock_callstats.write.TCPretry, - stats->sock_callstats.write.cwnd, - stats->sock_callstats.write.rtt, + stats->sock_callstats.write.tcpstats.retry, + stats->sock_callstats.write.tcpstats.cwnd, + stats->sock_callstats.write.tcpstats.rtt, stats->isochstats.cntFrames, stats->isochstats.cntFramesMissed, stats->isochstats.cntSlips, netpower_buf); } else { @@ -482,8 +598,8 @@ outbuffer, outbufferext, stats->sock_callstats.write.WriteCnt, stats->sock_callstats.write.WriteErr, - stats->sock_callstats.write.TCPretry, - stats->sock_callstats.write.rtt, + stats->sock_callstats.write.tcpstats.retry, + stats->sock_callstats.write.tcpstats.rtt, stats->isochstats.cntFrames, stats->isochstats.cntFramesMissed, stats->isochstats.cntSlips, netpower_buf); } @@ -529,57 +645,16 @@ printf(report_bw_jitter_loss_format, stats->common->transferIDStr, stats->ts.iStart, stats->ts.iEnd, outbuffer, outbufferext, - stats->jitter*1000.0, stats->cntError, stats->cntDatagrams, + (stats->jitter * 1e3), stats->cntError, stats->cntDatagrams, (100.0 * stats->cntError) / stats->cntDatagrams); _output_outoforder(stats); fflush(stdout); } -void udp_output_read_enhanced (struct TransferInfo *stats) { - HEADING_PRINT_COND(report_bw_jitter_loss_enhanced); - _print_stats_common(stats); - if (!stats->cntIPG) { - printf(report_bw_jitter_loss_suppress_enhanced_format, stats->common->transferIDStr, - stats->ts.iStart, stats->ts.iEnd, - outbuffer, outbufferext, - 0.0, stats->cntError, - stats->cntDatagrams, - 0.0,0.0,0.0,0.0,0.0,0.0); - } else { - if ((stats->transit.minTransit > UNREALISTIC_LATENCYMINMAX) || - (stats->transit.minTransit < UNREALISTIC_LATENCYMINMIN)) { - printf(report_bw_jitter_loss_suppress_enhanced_format, stats->common->transferIDStr, - stats->ts.iStart, stats->ts.iEnd, - outbuffer, outbufferext, - stats->jitter*1000.0, stats->cntError, stats->cntDatagrams, - (100.0 * stats->cntError) / stats->cntDatagrams, - (stats->cntIPG / stats->IPGsum)); - } else { - double meantransit = (stats->transit.cntTransit > 0) ? (stats->transit.sumTransit / stats->transit.cntTransit) : 0; - set_netpowerbuf(meantransit, stats); - printf(report_bw_jitter_loss_enhanced_format, stats->common->transferIDStr, - stats->ts.iStart, stats->ts.iEnd, - outbuffer, outbufferext, - stats->jitter*1000.0, stats->cntError, stats->cntDatagrams, - (100.0 * stats->cntError) / stats->cntDatagrams, - (meantransit * 1e3), - stats->transit.minTransit*1e3, - stats->transit.maxTransit*1e3, - (stats->transit.cntTransit < 2) ? 0 : sqrt(stats->transit.m2Transit / (stats->transit.cntTransit - 1)) / 1e3, - (stats->cntIPG / stats->IPGsum), - netpower_buf); - } - } - if (stats->latency_histogram) { - histogram_print(stats->latency_histogram, stats->ts.iStart, stats->ts.iEnd); - } - _output_outoforder(stats); - fflush(stdout); -} - void udp_output_read_enhanced_triptime (struct TransferInfo *stats) { HEADING_PRINT_COND(report_bw_jitter_loss_enhanced_triptime); _print_stats_common(stats); + if (!stats->cntIPG) { printf(report_bw_jitter_loss_suppress_enhanced_format, stats->common->transferIDStr, stats->ts.iStart, stats->ts.iEnd, @@ -588,29 +663,30 @@ stats->cntDatagrams, 0.0,0.0,0.0,0.0,0.0,0.0); } else { - if ((stats->transit.minTransit > UNREALISTIC_LATENCYMINMAX) || - (stats->transit.minTransit < UNREALISTIC_LATENCYMINMIN)) { + if ((stats->transit.current.min > UNREALISTIC_LATENCYMINMAX) || + (stats->transit.current.min < UNREALISTIC_LATENCYMINMIN)) { printf(report_bw_jitter_loss_suppress_enhanced_format, stats->common->transferIDStr, stats->ts.iStart, stats->ts.iEnd, outbuffer, outbufferext, - stats->jitter*1000.0, stats->cntError, stats->cntDatagrams, + (stats->jitter * 1e3), stats->cntError, stats->cntDatagrams, (100.0 * stats->cntError) / stats->cntDatagrams, (stats->cntIPG / stats->IPGsum)); } else { - double meantransit = (stats->transit.cntTransit > 0) ? (stats->transit.sumTransit / stats->transit.cntTransit) : 0; + double meantransit = (stats->transit.current.cnt > 0) ? (stats->transit.current.sum / stats->transit.current.cnt) : 0; int lambda = ((stats->IPGsum > 0.0) ? (round (stats->cntIPG / stats->IPGsum)) : 0.0); - double variance = (stats->transit.cntTransit < 2) ? 0 : (sqrt(stats->transit.m2Transit / (stats->transit.cntTransit - 1)) / 1e6); + double variance = (stats->transit.current.cnt < 2) ? 0 : \ + (sqrt(stats->transit.current.m2 / (stats->transit.current.cnt - 1))); set_llawbuf_udp(lambda, meantransit, variance, stats); set_netpowerbuf(meantransit, stats); printf(report_bw_jitter_loss_enhanced_triptime_format, stats->common->transferIDStr, stats->ts.iStart, stats->ts.iEnd, outbuffer, outbufferext, - stats->jitter*1000.0, stats->cntError, stats->cntDatagrams, + (stats->jitter * 1e3), stats->cntError, stats->cntDatagrams, (100.0 * stats->cntError) / stats->cntDatagrams, (meantransit * 1e3), - stats->transit.minTransit*1e3, - stats->transit.maxTransit*1e3, - (stats->transit.cntTransit < 2) ? 0 : sqrt(stats->transit.m2Transit / (stats->transit.cntTransit - 1)) / 1e3, + stats->transit.current.min * 1e3, + stats->transit.current.max * 1e3, + (stats->transit.current.cnt < 2) ? 0 : (1e3 * variance), // convert from sec to ms (stats->cntIPG / stats->IPGsum), llaw_buf, netpower_buf); @@ -637,29 +713,29 @@ // If the min latency is out of bounds of a realistic value // assume the clocks are not synched and suppress the // latency output - if ((stats->transit.minTransit > UNREALISTIC_LATENCYMINMAX) || - (stats->transit.minTransit < UNREALISTIC_LATENCYMINMIN)) { + if ((stats->transit.current.min > UNREALISTIC_LATENCYMINMAX) || + (stats->transit.current.min < UNREALISTIC_LATENCYMINMIN)) { printf(report_bw_jitter_loss_suppress_enhanced_format, stats->common->transferIDStr, stats->ts.iStart, stats->ts.iEnd, outbuffer, outbufferext, - stats->jitter*1000.0, stats->cntError, stats->cntDatagrams, + (stats->jitter * 1e3), stats->cntError, stats->cntDatagrams, (100.0 * stats->cntError) / stats->cntDatagrams, (stats->cntIPG / stats->IPGsum)); } else { - double meantransit = (stats->transit.cntTransit > 0) ? (stats->transit.sumTransit / stats->transit.cntTransit) : 0; + double meantransit = (stats->transit.current.cnt > 0) ? (stats->transit.current.sum / stats->transit.current.cnt) : 0; int lambda = ((stats->IPGsum > 0.0) ? (round (stats->cntIPG / stats->IPGsum)) : 0.0); - double variance = (stats->transit.cntTransit < 2) ? 0 : (sqrt(stats->transit.m2Transit / (stats->transit.cntTransit - 1)) / 1e6); + double variance = (stats->transit.current.cnt < 2) ? 0 : (sqrt(stats->transit.current.m2 / (stats->transit.current.cnt - 1))); set_llawbuf_udp(lambda, meantransit, variance, stats); set_netpowerbuf(meantransit, stats); printf(report_bw_jitter_loss_enhanced_isoch_format, stats->common->transferIDStr, stats->ts.iStart, stats->ts.iEnd, outbuffer, outbufferext, - stats->jitter*1e3, stats->cntError, stats->cntDatagrams, + (stats->jitter * 1e3), stats->cntError, stats->cntDatagrams, (100.0 * stats->cntError) / stats->cntDatagrams, (meantransit * 1e3), - stats->transit.minTransit*1e3, - stats->transit.maxTransit*1e3, - (stats->transit.cntTransit < 2) ? 0 : sqrt(stats->transit.m2Transit / (stats->transit.cntTransit - 1)) / 1e3, + stats->transit.current.min * 1e3, + stats->transit.current.max * 1e3, + (stats->transit.current.cnt < 2) ? 0 : 1e3 * (sqrt(stats->transit.current.m2 / (stats->transit.current.cnt - 1))), (stats->cntIPG / stats->IPGsum), llaw_buf, netpower_buf, @@ -770,7 +846,7 @@ printf(report_sumcnt_bw_read_enhanced_format, stats->threadcnt, stats->ts.iStart, stats->ts.iEnd, outbuffer, outbufferext, - stats->jitter*1000.0, stats->cntError, stats->cntDatagrams, + (stats->jitter * 1e3), stats->cntError, stats->cntDatagrams, (100.0 * stats->cntError) / stats->cntDatagrams); if (stats->cntOutofOrder > 0) { if (isSumOnly(stats->common)) { @@ -907,7 +983,7 @@ stats->sock_callstats.write.WriteCnt, stats->sock_callstats.write.WriteErr #if HAVE_TCP_STATS - ,stats->sock_callstats.write.TCPretry + ,stats->sock_callstats.write.tcpstats.retry #endif ); fflush(stdout); @@ -921,7 +997,7 @@ stats->sock_callstats.write.WriteCnt, stats->sock_callstats.write.WriteErr #if HAVE_TCP_STATS - ,stats->sock_callstats.write.TCPretry + ,stats->sock_callstats.write.tcpstats.retry #endif ); fflush(stdout); @@ -1010,7 +1086,7 @@ stats->ts.iEnd, stats->cntBytes, speed, - stats->jitter*1000.0, + (stats->jitter * 1e3), stats->cntError, stats->cntDatagrams, (100.0 * stats->cntError) / stats->cntDatagrams, stats->cntOutofOrder ); @@ -1116,6 +1192,13 @@ if (isOverrideTOS(report->common)) { fprintf(stdout, "Reflected TOS will be set to 0x%x\n", report->common->RTOS); } + if (isPrintMSS(report->common)) { + if (isTCPMSS(report->common)) { + printf(report_mss, report->sockmaxseg); + } else { + printf(report_default_mss, report->sockmaxseg); + } + } if (report->common->TOS) { fprintf(stdout, "TOS will be set to 0x%x\n", report->common->TOS); } @@ -1168,8 +1251,8 @@ if ((isEnhanced(report->common) || isNearCongest(report->common)) && !isUDP(report->common)) { byte_snprintf(outbuffer, sizeof(outbuffer), report->common->BufLen, 'B'); outbuffer[(sizeof(outbuffer)-1)] = '\0'; - if (isTcpDrain(report->common)) { - printf("%s: %s (drain-enabled)\n", client_write_size, outbuffer); + if (isTcpWriteTimes(report->common)) { + printf("%s: %s (writetimer-enabled)\n", client_write_size, outbuffer); } else { printf("%s: %s\n", client_write_size, outbuffer); } @@ -1190,15 +1273,27 @@ } if (isBounceBack(report->common)) { char tmpbuf[40]; - byte_snprintf(tmpbuf, sizeof(tmpbuf), report->common->BurstSize, 'A'); + byte_snprintf(tmpbuf, sizeof(tmpbuf), report->common->bbsize, 'A'); tmpbuf[39]='\0'; - printf(client_bounceback, tmpbuf); + if (isTcpQuickAck(report->common)) { + printf(client_bounceback, tmpbuf, report->common->bbhold); + } else { + printf(client_bounceback_noqack, tmpbuf, report->common->bbhold); + } } if (isFQPacing(report->common)) { byte_snprintf(outbuffer, sizeof(outbuffer), report->common->FQPacingRate, 'a'); outbuffer[(sizeof(outbuffer)-1)] = '\0'; printf(client_fq_pacing,outbuffer); } + if (isPrintMSS(report->common)) { + if (isTCPMSS(report->common)) { + printf(report_mss, report->sockmaxseg); + } else { + printf(report_default_mss, report->sockmaxseg); + } + } + if (isCongestionControl(report->common) && report->common->Congestion) { fprintf(stdout, "TCP congestion control set to %s\n", report->common->Congestion); } @@ -1248,7 +1343,7 @@ void reporter_connect_printf_tcp_final (struct ConnectionInfo * report) { if (report->connect_times.cnt > 1) { - double variance = (report->connect_times.cnt < 2) ? 0 : sqrt(report->connect_times.m2 / (report->connect_times.cnt - 1)); + double variance = (report->connect_times.cnt < 2) ? 0 : 1e3* (sqrt(report->connect_times.m2 / (report->connect_times.cnt - 1))); fprintf(stdout, "[ CT] final connect times (min/avg/max/stdev) = %0.3f/%0.3f/%0.3f/%0.3f ms (tot/err) = %d/%d\n", \ report->connect_times.min, \ (report->connect_times.sum / report->connect_times.cnt), \ @@ -1268,16 +1363,8 @@ struct sockaddr *peer = ((struct sockaddr*)&report->common->peer); outbuffer[0]='\0'; outbufferext[0]='\0'; - outbufferext2[0]='\0'; char *b = &outbuffer[0]; - if (!isUDP(report->common) && (report->common->socket > 0) && (isPrintMSS(report->common) || isEnhanced(report->common))) { - if (isPrintMSS(report->common) && (report->MSS <= 0)) { - printf(report_mss_unsupported, report->MSS); - } else if (report->MSS != -1) { - snprintf(b, SNBUFFERSIZE-strlen(b), " (%s%d)", "MSS=", report->MSS); - b += strlen(b); - } - } + #if HAVE_DECL_TCP_WINDOW_CLAMP if (!isUDP(report->common) && isRxClamp(report->common)) { snprintf(b, SNBUFFERSIZE-strlen(b), " (%s%d)", "clamp=", report->common->ClampSize); @@ -1318,7 +1405,7 @@ b += strlen(b); } if (isBounceBack(report->common)) { - snprintf(b, SNBUFFERSIZE-strlen(b), " (bounce-back)"); + snprintf(b, SNBUFFERSIZE-strlen(b), " (bb len/hold=%d/%d)", report->common->bbsize, report->common->bbhold); b += strlen(b); } if (isL2LengthCheck(report->common)) { @@ -1345,10 +1432,12 @@ } b += strlen(b); } else if (report->common->TOS) { - if (isFullDuplex(report->common)) { + if (isFullDuplex(report->common) || isBounceBack(report->common)) { snprintf(b, SNBUFFERSIZE-strlen(b), " (tos rx/tx=0x%x/0x%x)", report->common->TOS, report->common->TOS); } else if (isReverse(report->common)) { snprintf(b, SNBUFFERSIZE-strlen(b), " (tos tx=0x%x)", report->common->TOS); + } else { + snprintf(b, SNBUFFERSIZE-strlen(b), " (tos rx=0x%x)", report->common->TOS); } b += strlen(b); } @@ -1358,20 +1447,28 @@ b += strlen(b); } } +#if HAVE_DECL_TCP_QUICKACK + if (isTcpQuickAck(report->common)) { + snprintf(b, SNBUFFERSIZE-strlen(b), " (qack)"); + b += strlen(b); + } +#endif +#if HAVE_TCP_STATS + if (!isUDP(report->common) && (report->tcpinitstats.isValid)) { + snprintf(b, SNBUFFERSIZE-strlen(b), " (icwnd/mss/irtt=%u/%u/%u)", \ + report->tcpinitstats.cwnd, report->tcpinitstats.mss_negotiated, report->tcpinitstats.rtt); + b += strlen(b); + } +#endif + if (!isServerReverse(report->common) && (isEnhanced(report->common) || isConnectOnly(report->common))) { if (report->connect_timestamp.tv_sec > 0) { struct tm ts; ts = *localtime(&report->connect_timestamp.tv_sec); char now_timebuf[80]; strftime(now_timebuf, sizeof(now_timebuf), "%Y-%m-%d %H:%M:%S (%Z)", &ts); - if (!isUDP(report->common) && (report->common->ThreadMode == kMode_Client)) { -#if HAVE_TCP_STATS - if (report->init_cond.connecttime > 0.0) { - snprintf(b, SNBUFFERSIZE-strlen(b), " (irtt/icwnd=%u/%u)", report->init_cond.rtt, report->init_cond.cwnd); - b += strlen(b); - } -#endif - snprintf(b, SNBUFFERSIZE-strlen(b), " (ct=%4.2f ms) on %s", report->init_cond.connecttime, now_timebuf); + if (!isUDP(report->common) && (report->common->ThreadMode == kMode_Client) && (report->tcpinitstats.connecttime > 0)) { + snprintf(b, SNBUFFERSIZE-strlen(b), " (ct=%4.2f ms) on %s", report->tcpinitstats.connecttime, now_timebuf); } else { snprintf(b, SNBUFFERSIZE-strlen(b), " on %s", now_timebuf); } @@ -1539,7 +1636,7 @@ if (!isEnhanced(report->info.common)) { udp_output_read(&report->info); } else { - udp_output_read_enhanced(&report->info); + udp_output_read_enhanced_triptime(&report->info); } fflush(stdout); } diff -Nru iperf-2.1.5+dfsg1/src/Reports.c iperf-2.1.7+dfsg1/src/Reports.c --- iperf-2.1.5+dfsg1/src/Reports.c 2021-12-05 20:49:56.000000000 +0000 +++ iperf-2.1.7+dfsg1/src/Reports.c 2022-04-11 18:13:03.000000000 +0000 @@ -54,6 +54,7 @@ #include "headers.h" #include #include "Settings.hpp" +#include "PerfSocket.hpp" #include "Reporter.h" #include "Locale.h" #include "active_hosts.h" @@ -82,16 +83,15 @@ static void common_copy (struct ReportCommon **common, struct thread_Settings *inSettings) { // Do deep copies from settings *common = (struct ReportCommon *) calloc(1, sizeof(struct ReportCommon)); - int bytecnt = 0; - bytecnt = my_str_copy(&(*common)->Host, inSettings->mHost); - bytecnt += my_str_copy(&(*common)->HideHost, inSettings->mHideHost); - bytecnt += my_str_copy(&(*common)->Localhost, inSettings->mLocalhost); - bytecnt += my_str_copy(&(*common)->Ifrname, inSettings->mIfrname); - bytecnt += my_str_copy(&(*common)->Ifrnametx, inSettings->mIfrnametx); - bytecnt += my_str_copy(&(*common)->SSMMulticastStr, inSettings->mSSMMulticastStr); - bytecnt += my_str_copy(&(*common)->Congestion, inSettings->mCongestion); - bytecnt += my_str_copy(&(*common)->transferIDStr, inSettings->mTransferIDStr); - bytecnt += my_str_copy(&(*common)->PermitKey, inSettings->mPermitKey); + my_str_copy(&(*common)->Host, inSettings->mHost); + my_str_copy(&(*common)->HideHost, inSettings->mHideHost); + my_str_copy(&(*common)->Localhost, inSettings->mLocalhost); + my_str_copy(&(*common)->Ifrname, inSettings->mIfrname); + my_str_copy(&(*common)->Ifrnametx, inSettings->mIfrnametx); + my_str_copy(&(*common)->SSMMulticastStr, inSettings->mSSMMulticastStr); + my_str_copy(&(*common)->Congestion, inSettings->mCongestion); + my_str_copy(&(*common)->transferIDStr, inSettings->mTransferIDStr); + my_str_copy(&(*common)->PermitKey, inSettings->mPermitKey); // copy some relevant settings (*common)->flags = inSettings->flags; @@ -134,6 +134,8 @@ (*common)->FPS = inSettings->mFPS; (*common)->TOS = inSettings->mTOS; (*common)->RTOS = inSettings->mRTOS; + (*common)->bbsize = inSettings->mBounceBackBytes; + (*common)->bbhold = inSettings->mBounceBackHold; #if HAVE_DECL_TCP_WINDOW_CLAMP (*common)->ClampSize = inSettings->mClampSize; #endif @@ -377,6 +379,9 @@ if (ireport->info.framelatency_histogram) { histogram_delete(ireport->info.framelatency_histogram); } + if (ireport->info.bbrtt_histogram) { + histogram_delete(ireport->info.bbrtt_histogram); + } free_common_copy(ireport->info.common); free(ireport); } @@ -545,7 +550,7 @@ else ireport->info.output_handler = udp_output_read_enhanced_triptime; } else if (isEnhanced(inSettings)) { - ireport->info.output_handler = udp_output_read_enhanced; + ireport->info.output_handler = udp_output_read_enhanced_triptime; } else if (isFullDuplex(inSettings)) { ireport->info.output_handler = udp_output_read; } else { @@ -567,6 +572,10 @@ ireport->packet_handler_post_report = NULL; } else if (inSettings->mReportMode == kReport_CSV) { ireport->info.output_handler = tcp_output_basic_csv; + } else if (isBounceBack(inSettings)) { + ireport->packet_handler_post_report = reporter_handle_packet_bb_server; + ireport->transfer_protocol_handler = reporter_transfer_protocol_server_bb_tcp; + ireport->info.output_handler = tcp_output_write; } else if (isSumOnly(inSettings)) { ireport->info.output_handler = NULL; } else if (isTripTime(inSettings)) { @@ -603,12 +612,14 @@ ireport->info.output_handler = tcp_output_basic_csv; } else if (isSumOnly(inSettings)) { ireport->info.output_handler = NULL; -#if HAVE_DECL_TCP_NOTSENT_LOWAT - } else if (isTcpDrain(inSettings)) { - ireport->info.output_handler = tcp_output_write_enhanced_drain; -#endif + } else if (isBounceBack(inSettings)) { + ireport->packet_handler_post_report = reporter_handle_packet_bb_client; + ireport->transfer_protocol_handler = reporter_transfer_protocol_client_bb_tcp; + ireport->info.output_handler = tcp_output_write_bb; } else if (isIsochronous(inSettings)) { ireport->info.output_handler = tcp_output_write_enhanced_isoch; + } else if (isTcpWriteTimes(inSettings)) { + ireport->info.output_handler = tcp_output_write_enhanced_write; } else if (isEnhanced(inSettings)) { ireport->info.output_handler = tcp_output_write_enhanced; } else if (isFullDuplex(inSettings)) { @@ -648,20 +659,29 @@ ireport->info.latency_histogram = histogram_init(inSettings->mHistBins,inSettings->mHistBinsize,0,\ pow(10,inSettings->mHistUnits), \ inSettings->mHistci_lower, inSettings->mHistci_upper, ireport->info.common->transferID, name); - } else if ((inSettings->mThreadMode == kMode_Client) && isTcpDrain(inSettings) && isHistogram(inSettings) && !isUDP(inSettings)) { - char name[] = "D8"; + } else if ((inSettings->mThreadMode == kMode_Client) && isTcpWriteTimes(inSettings) && isHistogram(inSettings) && !isUDP(inSettings)) { + char name[] = "W8"; inSettings->mHistBins = 100000; // 10 seconds wide inSettings->mHistBinsize = 100; // 100 usec bins inSettings->mHistUnits = 6; // usecs 10 pow(x) inSettings->mHistci_lower = 5; inSettings->mHistci_upper = 95; -#if HAVE_DECL_TCP_NOTSENT_LOWAT - ireport->info.drain_histogram = histogram_init(inSettings->mHistBins,inSettings->mHistBinsize,0, \ + ireport->info.write_histogram = histogram_init(inSettings->mHistBins,inSettings->mHistBinsize,0, \ pow(10,inSettings->mHistUnits), \ inSettings->mHistci_lower, inSettings->mHistci_upper, ireport->info.common->transferID, name); -#endif } #endif + if ((inSettings->mThreadMode == kMode_Client) && isBounceBack(inSettings)) { + char name[] = "BB8"; + inSettings->mHistBins = 100000; // 10 seconds wide + inSettings->mHistBinsize = 100; // 100 usec bins + inSettings->mHistUnits = 6; // usecs 10 pow(x) + inSettings->mHistci_lower = 5; + inSettings->mHistci_upper = 95; + ireport->info.bbrtt_histogram = histogram_init(inSettings->mHistBins,inSettings->mHistBinsize,0, \ + pow(10,inSettings->mHistUnits), \ + inSettings->mHistci_lower, inSettings->mHistci_upper, ireport->info.common->transferID, name); + } return reporthdr; } @@ -685,7 +705,7 @@ * to achieve this. Such code will be easier to maintain * and to extend. */ -struct ReportHeader* InitConnectionReport (struct thread_Settings *inSettings, struct tcp_init_conditions *init_cond) { +struct ReportHeader* InitConnectionReport (struct thread_Settings *inSettings) { assert(inSettings != NULL); struct ReportHeader *reporthdr = (struct ReportHeader *) calloc(1, sizeof(struct ReportHeader)); if (reporthdr == NULL) { @@ -700,24 +720,9 @@ struct ConnectionInfo * creport = (struct ConnectionInfo *)(reporthdr->this_report); common_copy(&creport->common, inSettings); - if (!isUDP(inSettings) && (inSettings->mSock > 0) && !isDontRoute(inSettings) && \ - (inSettings->mThreadMode == kMode_Client) && \ - (init_cond && (init_cond->connecttime > 0.0))) { - creport->MSS = getsock_tcp_mss(inSettings->mSock); - } else { - creport->MSS = -1; - } + tcpstats_copy(&creport->tcpinitstats, &inSettings->tcpinitstats); // Fill out known fields for the connection report reporter_peerversion(creport, inSettings->peer_version_u, inSettings->peer_version_l); - if (init_cond) { - creport->init_cond.connecttime = init_cond->connecttime; - creport->init_cond.rtt = init_cond->rtt; - creport->init_cond.cwnd = init_cond->cwnd; - } else { - creport->init_cond.connecttime = -1; - creport->init_cond.rtt = -1; - creport->init_cond.cwnd = -1; - } if (isEnhanced(inSettings) && isTxStartTime(inSettings)) { creport->epochStartTime.tv_sec = inSettings->txstart_epoch.tv_sec; creport->epochStartTime.tv_usec = inSettings->txstart_epoch.tv_usec; @@ -782,6 +787,13 @@ sreport->isochstats.mVariance = inSettings->mVariance/8; sreport->isochstats.mBurstIPG = (unsigned int) (inSettings->mBurstIPG*1000.0); sreport->isochstats.mBurstInterval = (unsigned int) (1 / inSettings->mFPS * 1000000); + if (!isUDP(inSettings)) { + if (inSettings->mMSS > 0) { + sreport->sockmaxseg = inSettings->mMSS; + } else if (isPrintMSS(inSettings) && !(inSettings->mMSS > 0)) { + sreport->sockmaxseg = getsock_tcp_mss(inSettings->mSock); + } + } #ifdef HAVE_THREAD_DEBUG char rs[REPORTTXTMAX]; reporttype_text(reporthdr, &rs[0]); @@ -844,19 +856,20 @@ } if ((flags & SERVER_HEADER_EXTEND) != 0) { setEnhanced(stats->common); - stats->transit.minTransit = ntohl(server->extend.minTransit1); - stats->transit.minTransit += ntohl(server->extend.minTransit2) / (double)rMillion; - stats->transit.maxTransit = ntohl(server->extend.maxTransit1); - stats->transit.maxTransit += ntohl(server->extend.maxTransit2) / (double)rMillion; - stats->transit.sumTransit = ntohl(server->extend.sumTransit1); - stats->transit.sumTransit += ntohl(server->extend.sumTransit2) / (double)rMillion; - stats->transit.meanTransit = ntohl(server->extend.meanTransit1); - stats->transit.meanTransit += ntohl(server->extend.meanTransit2) / (double)rMillion; - stats->transit.m2Transit = ntohl(server->extend.m2Transit1); - stats->transit.m2Transit += ntohl(server->extend.m2Transit2) / (double)rMillion; - stats->transit.vdTransit = ntohl(server->extend.vdTransit1); - stats->transit.vdTransit += ntohl(server->extend.vdTransit2) / (double)rMillion; - stats->transit.cntTransit = ntohl(server->extend.cntTransit); + stats->transit.current.min = ntohl(server->extend.minTransit1); + stats->transit.current.min += ntohl(server->extend.minTransit2) / (double)rMillion; + stats->transit.current.max = ntohl(server->extend.maxTransit1); + stats->transit.current.max += ntohl(server->extend.maxTransit2) / (double)rMillion; + stats->transit.current.sum = ntohl(server->extend.sumTransit1); + stats->transit.current.sum += ntohl(server->extend.sumTransit2) / (double)rMillion; + stats->transit.current.mean = ntohl(server->extend.meanTransit1); + stats->transit.current.mean += ntohl(server->extend.meanTransit2) / (double)rMillion; + stats->transit.current.m2 = ntohl(server->extend.m2Transit1); + stats->transit.current.m2 += ntohl(server->extend.m2Transit2) / (double)rMillion; + stats->transit.current.m2 *= 1e-12; + stats->transit.current.vd = ntohl(server->extend.vdTransit1); + stats->transit.current.vd += ntohl(server->extend.vdTransit2) / (double)rMillion; + stats->transit.current.cnt = ntohl(server->extend.cntTransit); stats->cntIPG = ntohl(server->extend.cntIPG); stats->IPGsum = ntohl(server->extend.IPGsum); } else { @@ -880,13 +893,13 @@ void write_UDP_AckFIN (struct TransferInfo *stats, int len) { assert(stats!= NULL); int ackpacket_length = (int) (sizeof(struct UDP_datagram) + sizeof(struct server_hdr)); - char *ackPacket = (char *) calloc(1, len); + int readlen = ((ackpacket_length * 2) > len * 2) ? (ackpacket_length * 2) : (len * 2); + char *ackPacket = (char *) calloc(1, readlen); int success = 0; assert(ackPacket); fd_set readSet; int rc = 1; struct timeval timeout; - int readlen = ((ackpacket_length * 2) > len * 2) ? (ackpacket_length * 2) : (len * 2); if (ackPacket) { struct UDP_datagram *UDP_Hdr = (struct UDP_datagram *)ackPacket; @@ -919,19 +932,20 @@ hdr->base.jitter1 = htonl((long) stats->jitter); hdr->base.jitter2 = htonl((long) ((stats->jitter - (long)stats->jitter) * rMillion)); - hdr->extend.minTransit1 = htonl((long) stats->transit.totminTransit); - hdr->extend.minTransit2 = htonl((long) ((stats->transit.totminTransit - (long)stats->transit.totminTransit) * rMillion)); - hdr->extend.maxTransit1 = htonl((long) stats->transit.totmaxTransit); - hdr->extend.maxTransit2 = htonl((long) ((stats->transit.totmaxTransit - (long)stats->transit.totmaxTransit) * rMillion)); - hdr->extend.sumTransit1 = htonl((long) stats->transit.totsumTransit); - hdr->extend.sumTransit2 = htonl((long) ((stats->transit.totsumTransit - (long)stats->transit.totsumTransit) * rMillion)); - hdr->extend.meanTransit1 = htonl((long) stats->transit.totmeanTransit); - hdr->extend.meanTransit2 = htonl((long) ((stats->transit.totmeanTransit - (long)stats->transit.totmeanTransit) * rMillion)); - hdr->extend.m2Transit1 = htonl((long) stats->transit.totm2Transit); - hdr->extend.m2Transit2 = htonl((long) ((stats->transit.totm2Transit - (long)stats->transit.totm2Transit) * rMillion)); - hdr->extend.vdTransit1 = htonl((long) stats->transit.totvdTransit); - hdr->extend.vdTransit2 = htonl((long) ((stats->transit.totvdTransit - (long)stats->transit.totvdTransit) * rMillion)); - hdr->extend.cntTransit = htonl(stats->transit.totcntTransit); + hdr->extend.minTransit1 = htonl((long) stats->transit.total.min); + hdr->extend.minTransit2 = htonl((long) ((stats->transit.total.min - (long)stats->transit.total.min) * rMillion)); + hdr->extend.maxTransit1 = htonl((long) stats->transit.total.max); + hdr->extend.maxTransit2 = htonl((long) ((stats->transit.total.max - (long)stats->transit.total.max) * rMillion)); + hdr->extend.sumTransit1 = htonl((long) stats->transit.total.sum); + hdr->extend.sumTransit2 = htonl((long) ((stats->transit.total.sum - (long)stats->transit.total.sum) * rMillion)); + hdr->extend.meanTransit1 = htonl((long) stats->transit.total.mean); + hdr->extend.meanTransit2 = htonl((long) ((stats->transit.total.mean - (long)stats->transit.total.mean) * rMillion)); + stats->transit.total.m2 *= 1e12; + hdr->extend.m2Transit1 = htonl((long) stats->transit.total.m2); + hdr->extend.m2Transit2 = htonl((long) ((stats->transit.total.m2 - (long)stats->transit.total.m2) * rMillion)); + hdr->extend.vdTransit1 = htonl((long) stats->transit.total.vd); + hdr->extend.vdTransit2 = htonl((long) ((stats->transit.total.vd - (long)stats->transit.total.vd) * rMillion)); + hdr->extend.cntTransit = htonl(stats->transit.total.cnt); hdr->extend.cntIPG = htonl((long) (stats->cntDatagrams / (stats->ts.iEnd - stats->ts.iStart))); hdr->extend.IPGsum = htonl(1); diff -Nru iperf-2.1.5+dfsg1/src/Server.cpp iperf-2.1.7+dfsg1/src/Server.cpp --- iperf-2.1.5+dfsg1/src/Server.cpp 2021-12-05 20:49:56.000000000 +0000 +++ iperf-2.1.7+dfsg1/src/Server.cpp 2022-04-11 18:13:03.000000000 +0000 @@ -177,6 +177,15 @@ if (burst_nleft > 0) readLen = (mSettings->mBufLen < burst_nleft) ? mSettings->mBufLen : burst_nleft; reportstruct->emptyreport=1; +#if HAVE_DECL_TCP_QUICKACK + if (isTcpQuickAck(mSettings)) { + int opt = 1; + Socklen_t len = sizeof(opt); + int rc = setsockopt(mySocket, IPPROTO_TCP, TCP_QUICKACK, + reinterpret_cast(&opt), len); + WARN_errno(rc == SOCKET_ERROR, "setsockopt TCP_QUICKACK"); + } +#endif if (isburst && (burst_nleft == 0)) { if ((n = recvn(mSettings->mSock, reinterpret_cast(&burst_info), sizeof(struct TCP_burst_payload), 0)) == sizeof(struct TCP_burst_payload)) { // burst_info.typelen.type = ntohl(burst_info.typelen.type); @@ -279,7 +288,101 @@ FreeReport(myJob); } -void Server::RunTcpBounceBack () { +inline bool Server::ReadBBWithRXTimestamp () { + bool rc = false; + int n; + if ((n = recvn(mySocket, mSettings->mBuf, mSettings->mBounceBackBytes, 0)) == mSettings->mBounceBackBytes) { + struct bounceback_hdr *bbhdr = reinterpret_cast(mSettings->mBuf); + now.setnow(); + reportstruct->packetTime.tv_sec = now.getSecs(); + reportstruct->packetTime.tv_usec = now.getUsecs(); + reportstruct->emptyreport=0; + reportstruct->packetLen = mSettings->mBounceBackBytes; + // write the rx timestamp back into the payload + bbhdr->bbserverRx_ts.sec = htonl(reportstruct->packetTime.tv_sec); + bbhdr->bbserverRx_ts.usec = htonl(reportstruct->packetTime.tv_usec); + reportstruct->packetLen = mSettings->mBounceBackBytes; + rc = true; + } else if (n==0) { + peerclose = true; + } else { + reportstruct->emptyreport=1; + } + return rc; +} + +void Server::RunBounceBackTCP () { + if (!InitTrafficLoop()) + return; +#if HAVE_DECL_TCP_NODELAY + { + int nodelay = 1; + // set TCP nodelay option + int rc = setsockopt(mySocket, IPPROTO_TCP, TCP_NODELAY, + reinterpret_cast(&nodelay), sizeof(nodelay)); + WARN_errno(rc == SOCKET_ERROR, "setsockopt BB TCP_NODELAY"); + setNoDelay(mSettings); + } +#endif + if (mSettings->mInterval && (mSettings->mIntervalMode == kInterval_Time)) { + int sotimer = static_cast(round(mSettings->mInterval / 2.0)); + SetSocketOptionsSendTimeout(mSettings, sotimer); + } else if (isModeTime(mSettings)) { + int sotimer = static_cast(round(mSettings->mAmount * 10000) / 2); + SetSocketOptionsSendTimeout(mSettings, sotimer); + } + myReport->info.ts.prevsendTime = myReport->info.ts.startTime; + now.setnow(); + reportstruct->packetTime.tv_sec = now.getSecs(); + reportstruct->packetTime.tv_usec = now.getUsecs(); + reportstruct->packetLen = mSettings->mBounceBackBytes; + while (InProgress()) { + int n; + reportstruct->emptyreport=1; + do { + struct bounceback_hdr *bbhdr = reinterpret_cast(mSettings->mBuf); + if (mSettings->mBounceBackHold) { +#if HAVE_DECL_TCP_QUICKACK + if (isTcpQuickAck(mSettings)) { + int opt = 1; + Socklen_t len = sizeof(opt); + int rc = setsockopt(mySocket, IPPROTO_TCP, TCP_QUICKACK, + reinterpret_cast(&opt), len); + WARN_errno(rc == SOCKET_ERROR, "setsockopt TCP_QUICKACK"); + } +#endif + delay_loop(mSettings->mBounceBackHold); + } + now.setnow(); + bbhdr->bbserverTx_ts.sec = htonl(now.getSecs()); + bbhdr->bbserverTx_ts.usec = htonl(now.getUsecs()); + if (mSettings->mTOS) { + bbhdr->tos = htons((uint16_t)(mSettings->mTOS & 0xFF)); + } + if ((n = writen(mySocket, mSettings->mBuf, mSettings->mBounceBackBytes, &reportstruct->writecnt)) == mSettings->mBounceBackBytes) { + reportstruct->emptyreport=0; + reportstruct->packetLen += n; + ReportPacket(myReport, reportstruct); + } else { + break; + } + } while (ReadBBWithRXTimestamp()); + } + disarm_itimer(); + // stop timing + now.setnow(); + reportstruct->packetTime.tv_sec = now.getSecs(); + reportstruct->packetTime.tv_usec = now.getUsecs(); + reportstruct->packetLen = 0; + if (EndJob(myJob, reportstruct)) { +#if HAVE_THREAD_DEBUG + thread_debug("tcp close sock=%d", mySocket); +#endif + int rc = close(mySocket); + WARN_errno(rc == SOCKET_ERROR, "server close"); + } + Iperf_remove_host(mSettings); + FreeReport(myJob); } void Server::InitKernelTimeStamping () { @@ -432,6 +535,7 @@ } bool Server::InitTrafficLoop (void) { + bool UDPReady = true; myJob = InitIndividualReport(mSettings); myReport = static_cast(myJob->this_report); assert(myJob != NULL); @@ -473,19 +577,14 @@ if (setfullduplexflag) SetFullDuplexReportStartTime(); - if (isServerModeTime(mSettings) || (isModeTime(mSettings) && (isServerReverse(mSettings) || isFullDuplex(mSettings) || isReverse(mSettings)))) { + if (isServerModeTime(mSettings) || (isModeTime(mSettings) && (isBounceBack(mSettings) || isServerReverse(mSettings) || isFullDuplex(mSettings) || isReverse(mSettings)))) { + if (isServerReverse(mSettings) || isFullDuplex(mSettings) || isReverse(mSettings)) mSettings->mAmount += (SLOPSECS * 100); // add 2 sec for slop on reverse, units are 10 ms -#ifdef HAVE_SETITIMER - int err; - struct itimerval it; - memset (&it, 0, sizeof (it)); - it.it_value.tv_sec = static_cast(mSettings->mAmount / 100.0); - it.it_value.tv_usec = static_cast(10000 * (mSettings->mAmount - - it.it_value.tv_sec * 100.0)); - err = setitimer(ITIMER_REAL, &it, NULL); - FAIL_errno(err != 0, "setitimer", mSettings); -#endif + + int end_usecs (mSettings->mAmount * 10000); //amount units is 10 ms + if (int err = set_itimer(end_usecs)) + FAIL_errno(err != 0, "setitimer", mSettings); mEndTime.setnow(); mEndTime.add(mSettings->mAmount / 100.0); } @@ -497,7 +596,8 @@ reportstruct->frameID = 0; reportstruct->packetLen = mSettings->firstreadbytes; if (isUDP(mSettings)) { - ReadPacketID(); + int offset = 0; + UDPReady = !ReadPacketID(offset); reportstruct->packetTime = mSettings->accept_time; } else { reportstruct->sentTime.tv_sec = myReport->info.ts.startTime.tv_sec; @@ -506,7 +606,7 @@ } ReportPacket(myReport, reportstruct); } - return true; + return UDPReady; } inline int Server::ReadWithRxTimestamp () { @@ -549,10 +649,9 @@ } // Returns true if the client has indicated this is the final packet -inline bool Server::ReadPacketID () { +inline bool Server::ReadPacketID (int offset_adjust) { bool terminate = false; - struct UDP_datagram* mBuf_UDP = reinterpret_cast(mSettings->mBuf + mSettings->l4payloadoffset); - + struct UDP_datagram* mBuf_UDP = reinterpret_cast(mSettings->mBuf + offset_adjust); // terminate when datagram begins with negative index // the datagram ID should be correct, just negated @@ -712,6 +811,7 @@ reportstruct->frameID = ntohl(udp_pkt->isoch.frameid); reportstruct->prevframeID = ntohl(udp_pkt->isoch.prevframeid); reportstruct->burstsize = ntohl(udp_pkt->isoch.burstsize); + assert(reportstruct->burstsize > 0); reportstruct->burstperiod = ntohl(udp_pkt->isoch.burstperiod); reportstruct->remaining = ntohl(udp_pkt->isoch.remaining); if ((reportstruct->remaining == rxlen) && ((reportstruct->frameID - reportstruct->prevframeID) == 1)) { @@ -727,52 +827,51 @@ * ------------------------------------------------------------------- */ void Server::RunUDP () { int rxlen; - bool lastpacket = false; - - if (!InitTrafficLoop()) - return; + bool isLastPacket = false; - // Exit loop on three conditions - // 1) Fatal read error - // 2) Last packet of traffic flow sent by client - // 3) -t timer expires - while (InProgress() && !lastpacket) { - // The emptyreport flag can be set - // by any of the packet processing routines - // If it's set the iperf reporter won't do - // bandwidth accounting, basically it's indicating - // that the reportstruct itself couldn't be - // completely filled out. - reportstruct->emptyreport=1; - reportstruct->packetLen=0; - // read the next packet with timestamp - // will also set empty report or not - rxlen=ReadWithRxTimestamp(); - if (!peerclose && (rxlen > 0)) { - reportstruct->emptyreport = 0; - reportstruct->packetLen = rxlen; - if (isL2LengthCheck(mSettings)) { - reportstruct->l2len = rxlen; - // L2 processing will set the reportstruct packet length with the length found in the udp header - // and also set the expected length in the report struct. The reporter thread - // will do the compare and account and print l2 errors - reportstruct->l2errors = 0x0; - L2_processing(); - } - if (!(reportstruct->l2errors & L2UNKNOWN)) { - // ReadPacketID returns true if this is the last UDP packet sent by the client - // also sets the packet rx time in the reportstruct - reportstruct->prevSentTime = myReport->info.ts.prevsendTime; - reportstruct->prevPacketTime = myReport->info.ts.prevpacketTime; - lastpacket = ReadPacketID(); - myReport->info.ts.prevsendTime = reportstruct->sentTime; - myReport->info.ts.prevpacketTime = reportstruct->packetTime; - if (isIsochronous(mSettings)) { - udp_isoch_processing(rxlen); - } + if (InitTrafficLoop()) { + // Exit loop on three conditions + // 1) Fatal read error + // 2) Last packet of traffic flow sent by client + // 3) -t timer expires + while (InProgress() && !isLastPacket) { + // The emptyreport flag can be set + // by any of the packet processing routines + // If it's set the iperf reporter won't do + // bandwidth accounting, basically it's indicating + // that the reportstruct itself couldn't be + // completely filled out. + reportstruct->emptyreport=1; + reportstruct->packetLen=0; + // read the next packet with timestamp + // will also set empty report or not + rxlen=ReadWithRxTimestamp(); + if (!peerclose && (rxlen > 0)) { + reportstruct->emptyreport = 0; + reportstruct->packetLen = rxlen; + if (isL2LengthCheck(mSettings)) { + reportstruct->l2len = rxlen; + // L2 processing will set the reportstruct packet length with the length found in the udp header + // and also set the expected length in the report struct. The reporter thread + // will do the compare and account and print l2 errors + reportstruct->l2errors = 0x0; + L2_processing(); + } + if (!(reportstruct->l2errors & L2UNKNOWN)) { + // ReadPacketID returns true if this is the last UDP packet sent by the client + // also sets the packet rx time in the reportstruct + reportstruct->prevSentTime = myReport->info.ts.prevsendTime; + reportstruct->prevPacketTime = myReport->info.ts.prevpacketTime; + isLastPacket = ReadPacketID(mSettings->l4payloadoffset); + myReport->info.ts.prevsendTime = reportstruct->sentTime; + myReport->info.ts.prevpacketTime = reportstruct->packetTime; + if (isIsochronous(mSettings)) { + udp_isoch_processing(rxlen); + } + } } - } - ReportPacket(myReport, reportstruct); + ReportPacket(myReport, reportstruct); + } } disarm_itimer(); int do_close = EndJob(myJob, reportstruct); diff -Nru iperf-2.1.5+dfsg1/src/Settings.cpp iperf-2.1.7+dfsg1/src/Settings.cpp --- iperf-2.1.5+dfsg1/src/Settings.cpp 2021-12-05 20:50:04.000000000 +0000 +++ iperf-2.1.7+dfsg1/src/Settings.cpp 2022-04-11 18:13:03.000000000 +0000 @@ -71,6 +71,7 @@ #include "isochronous.hpp" #include "pdfs.h" #include "payloads.h" +#include "PerfSocket.hpp" #include static int reversetest = 0; @@ -107,8 +108,14 @@ static int tunif = 0; static int hideips = 0; static int bounceback = 0; -static int tcpdrain; -static int overridetos; +static int bouncebackhold = 0; +static int bouncebackperiod = 0; +static int overridetos = 0; +static int notcpbbquickack = 0; +static int tcpquickack = 0; +static int notcpbbquickack_cliset = 0; +static int congest = 0; +static int tcpwritetimes = 0; void Settings_Interpret(char option, const char *optarg, struct thread_Settings *mExtSettings); // apply compound settings after the command line has been fully parsed @@ -127,7 +134,7 @@ const struct option long_options[] = { {"singleclient", no_argument, NULL, '1'}, -#if 0 // to be released in 2.1.6 +#if 0 {"v4", no_argument, NULL, '4'}, {"v6", no_argument, NULL, '6'}, #endif @@ -156,9 +163,11 @@ // more esoteric options {"awdl", no_argument, NULL, 'A'}, {"bind", required_argument, NULL, 'B'}, -#if 0 // to be released in 2.1.6 -{"bounce-back", optional_argument, &bounceback, 1}, -#endif +{"bounceback", no_argument, &bounceback, 1}, +{"bounceback-congest", no_argument, &congest, 1}, +{"bounceback-hold", required_argument, &bouncebackhold, 1}, +{"bounceback-no-quickack", no_argument, ¬cpbbquickack, 1}, +{"bounceback-period", required_argument, &bouncebackperiod, 1}, {"compatibility", no_argument, NULL, 'C'}, {"daemon", no_argument, NULL, 'D'}, {"file_input", required_argument, NULL, 'F'}, @@ -207,10 +216,11 @@ {"permit-key-timeout", required_argument, &permitkeytimeout, 1}, {"burst-size", required_argument, &burstsize, 1}, {"burst-period", required_argument, &burstperiodic, 1}, -{"tcp-drain", no_argument, &tcpdrain, 1}, {"tos-override", required_argument, &overridetos, 1}, {"tcp-rx-window-clamp", required_argument, &rxwinclamp, 1}, +{"tcp-quickack", no_argument, &tcpquickack, 1}, {"tcp-write-prefetch", required_argument, &txnotsentlowwater, 1}, // see doc/DESIGN_NOTES +{"tcp-write-times", no_argument, &tcpwritetimes, 1}, {"tap-dev", optional_argument, &tapif, 1}, {"tun-dev", optional_argument, &tunif, 1}, {"NUM_REPORT_STRUCTS", required_argument, &numreportstructs, 1}, @@ -275,12 +285,9 @@ #define DEFAULTS() const long kDefault_UDPRate = 1024 * 1024; // -u if set, 1 Mbit/sec -const int kDefault_UDPBufLen = 1470; // -u if set, read/write 1470 bytes - -// v4: 1470 bytes UDP payload will fill one and only one ethernet datagram (IPv4 overhead is 20 bytes) -const int kDefault_UDPBufLenV6 = 1450; // -u if set, read/write 1470 bytes -// v6: 1450 bytes UDP payload will fill one and only one ethernet datagram (IPv6 overhead is 40 bytes) const int kDefault_TCPBufLen = 128 * 1024; // TCP default read/write size +const int kDefault_BBTCPBufLen = 100; // default bounce-back size in bytes + /* ------------------------------------------------------------------- * Initialize all settings to defaults. @@ -429,6 +436,8 @@ mbuflen += TAPBYTESSLOP; #endif (*into)->mBuf = new char[mbuflen]; + memset((*into)->mBuf, 0, mbuflen); + #ifdef HAVE_THREAD_DEBUG thread_debug("Copy Settings: MBUF malloc %d bytes (%p)", mbuflen, (void *) (*into)->mBuf); #endif @@ -623,7 +632,9 @@ break; case 'm': // print TCP MSS +#if HAVE_DECL_TCP_MAXSEG setPrintMSS(mExtSettings); +#endif break; case 'n': // bytes of data @@ -815,9 +826,11 @@ break; case 'M': // specify TCP MSS (maximum segment size) - mExtSettings->mMSS = byte_atoi(optarg); - setPrintMSS(mExtSettings); +#if HAVE_DECL_TCP_MAXSEG + mExtSettings->mMSS = byte_atoi(optarg); setTCPMSS(mExtSettings); + setPrintMSS(mExtSettings); +#endif break; case 'N': // specify TCP nodelay option (disable Jacobson's Algorithm) @@ -1083,14 +1096,23 @@ fprintf(stderr, "--tcp-rx-window-clamp not supported on this platform\n"); #endif } - if (tcpdrain) { - tcpdrain = 0; -#if HAVE_DECL_TCP_NOTSENT_LOWAT - setTcpDrain(mExtSettings); - setEnhanced(mExtSettings); -#else - fprintf(stderr, "--tcp-drain not supported on this platform\n"); -#endif + if (tcpwritetimes) { + tcpwritetimes = 0; + setTcpWriteTimes(mExtSettings); + } + if (notcpbbquickack) { + notcpbbquickack = 0; + notcpbbquickack_cliset = 1; + } + if (tcpquickack) { + tcpquickack = 0; +#if HAVE_DECL_TCP_QUICKACK + setTcpQuickAck(mExtSettings); +#endif + } + if (congest) { + congest= 0; + setCongest(mExtSettings); } if (txnotsentlowwater) { txnotsentlowwater = 0; @@ -1098,11 +1120,6 @@ mExtSettings->mWritePrefetch = byte_atoi(optarg); setWritePrefetch(mExtSettings); setEnhanced(mExtSettings); - mExtSettings->mHistBins = 100000; // 10 seconds wide - mExtSettings->mHistBinsize = 100; // 100 usec bins - mExtSettings->mHistUnits = 6; // usecs 10 pow(x) - mExtSettings->mHistci_lower = 5; - mExtSettings->mHistci_upper = 95; #else fprintf(stderr, "--tcp-write-prefetch not supported on this platform\n"); #endif @@ -1110,12 +1127,17 @@ if (burstperiodic) { burstperiodic = 0; setPeriodicBurst(mExtSettings); - if (optarg) { + if (optarg && (atof(optarg) > 1e-5)) { // limit to 10 usecs mExtSettings->mFPS = 1.0/atof(optarg); + } else { + if (atof(optarg) != 0) + fprintf(stderr, "WARN: burst-period too small, must be greater than 10 usecs\n"); + unsetPeriodicBurst(mExtSettings); } } if (burstsize) { burstsize = 0; + setPeriodicBurst(mExtSettings); if (optarg) { mExtSettings->mBurstSize = byte_atoi(optarg); } @@ -1159,6 +1181,27 @@ if (bounceback) { bounceback = 0; setBounceBack(mExtSettings); + setNoDelay(mExtSettings); + setEnhanced(mExtSettings); + } + if (bouncebackhold) { + bouncebackhold = 0; + if (optarg) + //cli units is ms, working units is us + mExtSettings->mBounceBackHold = int(atof(optarg) * 1e3); + else + mExtSettings->mBounceBackHold = 0; + } + if (bouncebackperiod) { + bouncebackperiod = 0; + setPeriodicBurst(mExtSettings); + if (optarg && (atof(optarg) > 1e-2)) { // limit to 10 usecs + mExtSettings->mFPS = 1e3/atof(optarg); // cli units is ms + } else { + if (atof(optarg) != 0) + fprintf(stderr, "WARN: bouncback-period too small, must be greater than 10 usecs\n"); + unsetPeriodicBurst(mExtSettings); + } } break; default: // ignore unknown @@ -1241,7 +1284,10 @@ mExtSettings->mBufLen = kDefault_UDPBufLen; } } else { - mExtSettings->mBufLen = kDefault_TCPBufLen; + if (isBounceBack(mExtSettings)) + mExtSettings->mBufLen = kDefault_BBTCPBufLen; + else + mExtSettings->mBufLen = kDefault_TCPBufLen; } } if (!mExtSettings->mPortLast) @@ -1382,10 +1428,25 @@ bail = true; } } - if (isBounceBack(mExtSettings) && (static_cast (mExtSettings->mBurstSize) < mExtSettings->mBufLen)) { - fprintf(stderr, "WARN: options of --burst-size for bounce-back is being set to -l length of %d\n", mExtSettings->mBufLen); + if (isBounceBack(mExtSettings)) { + if (static_cast (mExtSettings->mBurstSize) > 0) { + fprintf(stderr, "WARN: options of --burst-size for bounce-back ignored, use -l sets size\n"); + } + mExtSettings->mBounceBackBytes = mExtSettings->mBufLen; mExtSettings->mBurstSize = mExtSettings->mBufLen; +#if HAVE_DECL_TCP_QUICKACK + if (notcpbbquickack_cliset && isTcpQuickAck(mExtSettings)) { + fprintf(stderr, "ERROR: --tcp-quickack and --bounceback-no-quickack are mutually exclusive\n"); + bail = true; + } + // be wary of double negatives here + if (!notcpbbquickack_cliset && (mExtSettings->mBounceBackHold > 0)) { + setTcpQuickAck(mExtSettings); + } +#endif + } + if (isPeriodicBurst(mExtSettings)) { if (isIsochronous(mExtSettings)) { fprintf(stderr, "ERROR: options of --burst-period and --isochronous cannot be applied together\n"); @@ -1395,12 +1456,9 @@ bail = true; } else if (static_cast (mExtSettings->mBurstSize) == 0) { mExtSettings->mBurstSize = byte_atoi("1M"); //default to 1 Mbyte - } else if (isBounceBack(mExtSettings)) { - fprintf(stderr, "ERROR: options of --burst-period and --bounce-back cannot be applied together\n"); - bail = true; } if (static_cast (mExtSettings->mBurstSize) < mExtSettings->mBufLen) { - fprintf(stderr, "ERROR: option of --burst-size must be equal or larger to write length (-l)\n"); + fprintf(stderr, "ERROR: option of --burst-size %d must be equal or larger to write length (-l) %d\n", mExtSettings->mBurstSize, mExtSettings->mBufLen); bail = true; } } else if (!isBounceBack(mExtSettings) && (static_cast (mExtSettings->mBurstSize) > 0)) { @@ -1437,9 +1495,9 @@ fprintf(stderr, "WARN: setting of option --tcp-write-prefetch is not supported with -u UDP\n"); unsetWritePrefetch(mExtSettings); } - if (isTcpDrain(mExtSettings)) { - fprintf(stderr, "WARN: setting of option --tcp-drain is not supported with -u UDP\n"); - unsetTcpDrain(mExtSettings); + if (isTcpQuickAck(mExtSettings)) { + fprintf(stderr, "WARN: setting of option --tcp-quickack is not supported with -u UDP\n"); + unsetWritePrefetch(mExtSettings); } { @@ -1499,8 +1557,13 @@ bail = true; } } - if (!isReverse(mExtSettings) && !isFullDuplex(mExtSettings) && isHistogram(mExtSettings) && !isWritePrefetch(mExtSettings)) { - fprintf(stderr, "WARN: option of --histograms on the client requires --tcp-write-prefetch\n"); + if (!isReverse(mExtSettings) && !isFullDuplex(mExtSettings) && isHistogram(mExtSettings)){ + setTcpWriteTimes(mExtSettings); + mExtSettings->mHistBins = 100000; // 10 seconds wide + mExtSettings->mHistBinsize = 100; // 100 usec bins + mExtSettings->mHistUnits = 6; // usecs 10 pow(x) + mExtSettings->mHistci_lower = 5; + mExtSettings->mHistci_upper = 95; } if (isCongestionControl(mExtSettings) && isReverse(mExtSettings)) { fprintf(stderr, "ERROR: tcp congestion control -Z and --reverse cannot be applied together\n"); @@ -1547,10 +1610,6 @@ fprintf(stderr, "WARN: setting of option --tcp-write-prefetch is not supported on the server\n"); unsetWritePrefetch(mExtSettings); } - if (isTcpDrain(mExtSettings)) { - fprintf(stderr, "WARN: setting of option --tcp-drain is not supported on the server\n"); - unsetTcpDrain(mExtSettings); - } if (isIncrSrcIP(mExtSettings)) { fprintf(stderr, "WARN: setting of option --incr-srcip is not supported on the server\n"); } @@ -1910,8 +1969,14 @@ (*client)->mTID = thread_zeroid(); (*client)->mPort = static_cast(ntohl(hdr->mPort)); (*client)->mThreads = 1; + struct thread_Settings *tmpSettings = *client; if (hdr->mBufLen != 0) { (*client)->mBufLen = ntohl(hdr->mBufLen); + setBuflenSet(tmpSettings); + } else { +#ifdef DEFAULT_PAYLOAD_LEN_PER_MTU_DISCOVERY + checksock_max_udp_payload(tmpSettings); +#endif } (*client)->mAmount = ntohl(hdr->mAmount); if (((*client)->mAmount & 0x80000000) > 0) { @@ -2117,7 +2182,10 @@ // Check the corner case of small packets and trip times if (isUDP(client) && isSmallTripTime(client)) { + int buflen = (client->mBufLen < (int) sizeof(struct client_udpsmall_testhdr)) ? client->mBufLen \ + : sizeof(struct client_udpsmall_testhdr); struct client_udpsmall_testhdr *hdr = static_cast(testhdr); + memset(hdr, 0, buflen); hdr->flags = htons(HEADER16_SMALL_TRIPTIMES); #ifdef HAVE_THREAD_DEBUG thread_debug("UDP small trip times flags = %X", ntohs(hdr->flags)); @@ -2138,7 +2206,9 @@ // Now setup UDP and TCP specific passed settings from client to server if (isUDP(client)) { // UDP test information passed in every packet per being stateless struct client_udp_testhdr *hdr = static_cast(testhdr); - memset(hdr, 0, sizeof(struct client_udp_testhdr)); + int buflen = (client->mBufLen < (int) sizeof(struct client_udp_testhdr)) ? client->mBufLen \ + : sizeof(struct client_udp_testhdr); + memset(hdr, 0, buflen); flags |= HEADER_SEQNO64B; // use 64 bit by default flags |= HEADER_EXTEND; hdr->extend.version_u = htonl(IPERF_VERSION_MAJORHEX); @@ -2242,9 +2312,11 @@ #endif if (isBounceBack(client)) { flags = HEADER_BOUNCEBACK; - len = sizeof(struct bounce_back_datagram_hdr); + len = sizeof(struct bounceback_hdr); } else { - memset(hdr, 0, sizeof(struct client_tcp_testhdr)); + int buflen = (client->mBufLen < (int) sizeof(struct client_tcp_testhdr)) ? client->mBufLen \ + : sizeof(struct client_tcp_testhdr); + memset(hdr, 0, buflen); flags |= HEADER_EXTEND; hdr->extend.version_u = htonl(IPERF_VERSION_MAJORHEX); hdr->extend.version_l = htonl(IPERF_VERSION_MINORHEX); @@ -2287,6 +2359,11 @@ hdr->extend.TCPWritePrefetch = htonl((long)client->mWritePrefetch); } #endif +#if HAVE_DECL_TCP_QUICKACK + if (isTcpQuickAck(client) && (!isReverse(client) || isFullDuplex(client))) { + upperflags |= HEADER_TCPQUICKACK; + } +#endif if (isIsochronous(client) || isPeriodicBurst(client)) { if (isPeriodicBurst(client)) { upperflags |= HEADER_PERIODICBURST; // overload the isoch settings diff -Nru iperf-2.1.5+dfsg1/src/socket_io.c iperf-2.1.7+dfsg1/src/socket_io.c --- iperf-2.1.5+dfsg1/src/socket_io.c 2021-12-05 20:49:56.000000000 +0000 +++ iperf-2.1.7+dfsg1/src/socket_io.c 2022-04-11 18:13:03.000000000 +0000 @@ -58,65 +58,6 @@ #endif /* ------------------------------------------------------------------- - * If inMSS > 0, set the TCP maximum segment size for inSock. - * Otherwise leave it as the system default. - * ------------------------------------------------------------------- */ - -const char warn_mss_fail[] = "\ -WARNING: attempt to set TCP maxmimum segment size to %d failed.\n\ -Setting the MSS may not be implemented on this OS.\n"; - -const char warn_mss_notset[] = -"WARNING: attempt to set TCP maximum segment size to %d, but got %d\n"; - -void setsock_tcp_mss (int inSock, int inMSS) { -#ifdef TCP_MAXSEG - int rc; - int newMSS; - Socklen_t len; - - assert(inSock != INVALID_SOCKET); - - if (inMSS > 0) { - /* set */ - newMSS = inMSS; - len = sizeof(newMSS); - rc = setsockopt(inSock, IPPROTO_TCP, TCP_MAXSEG, (char*) &newMSS, len); - if (rc == SOCKET_ERROR) { - fprintf(stderr, warn_mss_fail, newMSS); - return; - } - - /* verify results */ - rc = getsockopt(inSock, IPPROTO_TCP, TCP_MAXSEG, (char*) &newMSS, &len); - WARN_errno(rc == SOCKET_ERROR, "getsockopt TCP_MAXSEG"); - if (newMSS != inMSS) { - fprintf(stderr, warn_mss_notset, inMSS, newMSS); - } - } -#endif -} /* end setsock_tcp_mss */ - -/* ------------------------------------------------------------------- - * returns the TCP maximum segment size - * ------------------------------------------------------------------- */ - -int getsock_tcp_mss (int inSock) { - int theMSS = -1; -#ifdef TCP_MAXSEG - int rc; - Socklen_t len; - assert(inSock >= 0); - - /* query for MSS */ - len = sizeof(theMSS); - rc = getsockopt(inSock, IPPROTO_TCP, TCP_MAXSEG, (char*)&theMSS, &len); - WARN_errno(rc == SOCKET_ERROR, "getsockopt TCP_MAXSEG"); -#endif - return theMSS; -} /* end getsock_tcp_mss */ - -/* ------------------------------------------------------------------- * Attempts to reads n bytes from a socket. * Returns number actually read, or -1 on error. * If number read < inLen then we reached EOF. @@ -171,7 +112,7 @@ nleft = inLen; #if (HAVE_DECL_MSG_PEEK) if (flags & MSG_PEEK) { - while (nleft != nread) { + while ((nleft != nread) && !sInterupted) { nread = recv(inSock, ptr, nleft, flags); switch (nread) { case SOCKET_ERROR : @@ -179,6 +120,7 @@ if (FATALTCPREADERR(errno)) { WARN_errno(1, "recvn peek"); nread = -1; + sInterupted = 1; goto DONE; } #ifdef HAVE_THREAD_DEBUG @@ -198,7 +140,7 @@ } else #endif { - while (nleft > 0) { + while ((nleft > 0) && !sInterupted) { #if (HAVE_DECL_MSG_WAITALL) nread = recv(inSock, ptr, nleft, MSG_WAITALL); #else @@ -210,6 +152,7 @@ if (FATALTCPREADERR(errno)) { WARN_errno(1, "recvn"); nread = -1; + sInterupted = 1; goto DONE; } #ifdef HAVE_THREAD_DEBUG @@ -258,7 +201,7 @@ nwritten = 0; *count = 0; - while (nleft > 0) { + while ((nleft > 0) && !sInterupted) { nwritten = write(inSock, ptr, nleft); (*count)++; switch (nwritten) { @@ -266,6 +209,7 @@ if (!(errno == EINTR) || (errno == EAGAIN) || (errno == EWOULDBLOCK)) { nwritten = inLen - nleft; WARN_errno(1, "writen fatal"); + sInterupted = 1; goto DONE; } break; diff -Nru iperf-2.1.5+dfsg1/src/tcp_window_size.c iperf-2.1.7+dfsg1/src/tcp_window_size.c --- iperf-2.1.5+dfsg1/src/tcp_window_size.c 2021-12-05 20:49:56.000000000 +0000 +++ iperf-2.1.7+dfsg1/src/tcp_window_size.c 2022-04-11 18:13:03.000000000 +0000 @@ -143,11 +143,10 @@ * ------------------------------------------------------------------- */ int getsock_tcp_windowsize (int inSock, int inSend) { - int theTCPWin = 0; + int rc = -1; #ifdef SO_SNDBUF - int rc; Socklen_t len; - + int theTCPWin = 0; /* send buffer -- query for buffer size */ len = sizeof(theTCPWin); if (inSend) { @@ -157,11 +156,11 @@ rc = getsockopt(inSock, SOL_SOCKET, SO_RCVBUF, (char*) &theTCPWin, &len); } - if (rc < 0) { - return rc; + if (rc == 0) { + rc = theTCPWin; } #endif - return theTCPWin; + return rc; } /* end getsock_tcp_windowsize */ #if HAVE_DECL_TCP_WINDOW_CLAMP diff -Nru iperf-2.1.5+dfsg1/t/base.sh iperf-2.1.7+dfsg1/t/base.sh --- iperf-2.1.5+dfsg1/t/base.sh 2021-12-05 20:49:56.000000000 +0000 +++ iperf-2.1.7+dfsg1/t/base.sh 2022-04-11 18:13:03.000000000 +0000 @@ -11,6 +11,8 @@ port=$(expr 5000 + $test) lport=$(expr 6000 - $test) +@() { echo "+ $@"; "$@"; } + run_iperf() { mode=server server=(-s) @@ -32,9 +34,9 @@ # Start client # Merge server and client output # Store results for additional processing and also copy to stderr for progress - results=$(src/iperf -p $port "${server[@]}" 2>&1 | { + results=$(@ src/iperf -p $port "${server[@]}" 2>&1 | { awk '{print};/listening/{exit 0}'; - src/iperf -p $port "${client[@]}"; cat; + @ src/iperf -p $port "${client[@]}"; cat; } 2>&1 | tee /dev/stderr) # Check for known error messages