Skip ab/6749736 in stage.
Merged-In: I13ef2d9019905b87dedd1c75ced9f762136d27b9
Change-Id: I3fb7578cf99ea7b43f5c966fe8de6f5a9299aa0c
diff --git a/METADATA b/METADATA
index 51a13c1..8199d28 100644
--- a/METADATA
+++ b/METADATA
@@ -9,11 +9,11 @@
type: GIT
value: "https://github.com/esnet/iperf.git"
}
- version: "3.8"
+ version: "3.9"
license_type: NOTICE
last_upgrade_date {
year: 2020
- month: 7
- day: 10
+ month: 8
+ day: 17
}
}
diff --git a/RELNOTES.md b/RELNOTES.md
index ae52d83..21f0801 100644
--- a/RELNOTES.md
+++ b/RELNOTES.md
@@ -1,6 +1,40 @@
iperf3 Release Notes
====================
+iperf 3.9 2020-08-17
+--------------------
+
+* Notable user-visible changes
+
+ * A --timestamps flag has been added, which prepends a timestamp to
+ each output line. An optional argument to this flag, which is a
+ format specification to strftime(3), allows for custom timestamp
+ formats (#909, #1028).
+
+ * A --server-bitrate-limit flag has been added as a server-side
+ command-line argument. It allows a server to enforce a maximum
+ throughput rate; client connections that specify a higher bitrate
+ or exceed this bitrate during a test will be terminated. The
+ bitrate is expressed in bits per second, with an optional trailing
+ slash and integer count that specifies an averaging interval over
+ which to enforce the limit (#999).
+
+ * A bug that caused increased CPU usage with the --bidir option has
+ been fixed (#1011).
+
+* Notable developer-visible changes
+
+ * Fixed various minor memory leaks (#1023).
+
+iperf 3.8.1 2020-06-10
+----------------------
+
+* Notable user-visible changes
+
+ * A regression with "make install", where the libiperf shared
+ library files were not getting installed, has been fixed (#1013 /
+ #1014).
+
iperf 3.8 2020-06-08
--------------------
@@ -32,7 +66,7 @@
* Notable developer-visible changes
- * The embedded version of cJSON has been updated to 1.3.17 (#978).
+ * The embedded version of cJSON has been updated to 1.7.13 (#978).
* Some server authentication functions have been added to the API
(#911).
diff --git a/aclocal.m4 b/aclocal.m4
index 9310d8b..df70996 100644
--- a/aclocal.m4
+++ b/aclocal.m4
@@ -1081,16 +1081,11 @@
_lt_dar_allow_undefined='$wl-undefined ${wl}suppress' ;;
darwin1.*)
_lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;;
- darwin*) # darwin 5.x on
- # if running on 10.5 or later, the deployment target defaults
- # to the OS version, if on x86, and 10.4, the deployment
- # target defaults to 10.4. Don't you love it?
- case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in
- 10.0,*86*-darwin8*|10.0,*-darwin[[91]]*)
- _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;;
- 10.[[012]][[,.]]*)
+ darwin*)
+ case ${MACOSX_DEPLOYMENT_TARGET},$host in
+ 10.[[012]],*|,*powerpc*)
_lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;;
- 10.*)
+ *)
_lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;;
esac
;;
diff --git a/config/iperf_config_static_bin.m4 b/config/iperf_config_static_bin.m4
index c72efe5..5229394 100644
--- a/config/iperf_config_static_bin.m4
+++ b/config/iperf_config_static_bin.m4
@@ -1,8 +1,9 @@
# Also link binaries as static
AC_ARG_ENABLE([static-bin],
AS_HELP_STRING([--enable-static-bin], [link iperf3 binary statically]),
- [enable_static_bin=yes
- AC_DISABLE_SHARED],
+ [enable_static=yes
+ enable_shared=no
+ enable_static_bin=yes],
[:])
AM_CONDITIONAL([ENABLE_STATIC_BIN], [test x$enable_static_bin = xno])
diff --git a/configure b/configure
index 1ebaffb..ee03ab2 100755
--- a/configure
+++ b/configure
@@ -1,6 +1,6 @@
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.69 for iperf 3.8.
+# Generated by GNU Autoconf 2.69 for iperf 3.9.
#
# Report bugs to <https://github.com/esnet/iperf>.
#
@@ -590,8 +590,8 @@
# Identity of this package.
PACKAGE_NAME='iperf'
PACKAGE_TARNAME='iperf'
-PACKAGE_VERSION='3.8'
-PACKAGE_STRING='iperf 3.8'
+PACKAGE_VERSION='3.9'
+PACKAGE_STRING='iperf 3.9'
PACKAGE_BUGREPORT='https://github.com/esnet/iperf'
PACKAGE_URL='https://software.es.net/iperf/'
@@ -763,8 +763,8 @@
ac_user_opts='
enable_option_checking
enable_static_bin
-enable_shared
enable_silent_rules
+enable_shared
enable_static
with_pic
enable_fast_install
@@ -1328,7 +1328,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 3.8 to adapt to many kinds of systems.
+\`configure' configures iperf 3.9 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
@@ -1398,7 +1398,7 @@
if test -n "$ac_init_help"; then
case $ac_init_help in
- short | recursive ) echo "Configuration of iperf 3.8:";;
+ short | recursive ) echo "Configuration of iperf 3.9:";;
esac
cat <<\_ACEOF
@@ -1407,9 +1407,9 @@
--disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no)
--enable-FEATURE[=ARG] include FEATURE [ARG=yes]
--enable-static-bin link iperf3 binary statically
- --enable-shared[=PKGS] build shared libraries [default=no]
--enable-silent-rules less verbose build output (undo: "make V=1")
--disable-silent-rules verbose build output (undo: "make V=0")
+ --enable-shared[=PKGS] build shared libraries [default=yes]
--enable-static[=PKGS] build static libraries [default=yes]
--enable-fast-install[=PKGS]
optimize for fast installation [default=yes]
@@ -1516,7 +1516,7 @@
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
-iperf configure 3.8
+iperf configure 3.9
generated by GNU Autoconf 2.69
Copyright (C) 2012 Free Software Foundation, Inc.
@@ -1885,7 +1885,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 3.8, which was
+It was created by iperf $as_me 3.9, which was
generated by GNU Autoconf 2.69. Invocation command line was
$ $0 $@
@@ -2293,38 +2293,9 @@
# Also link binaries as static
# Check whether --enable-static-bin was given.
if test "${enable_static_bin+set}" = set; then :
- enableval=$enable_static_bin; enable_static_bin=yes
- # Check whether --enable-shared was given.
-if test "${enable_shared+set}" = set; then :
- enableval=$enable_shared; p=${PACKAGE-default}
- case $enableval in
- yes) enable_shared=yes ;;
- no) enable_shared=no ;;
- *)
- enable_shared=no
- # Look at the argument we got. We use all the common list separators.
- lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR,
- for pkg in $enableval; do
- IFS=$lt_save_ifs
- if test "X$pkg" = "X$p"; then
- enable_shared=yes
- fi
- done
- IFS=$lt_save_ifs
- ;;
- esac
-else
- enable_shared=no
-fi
-
-
-
-
-
-
-
-
-
+ enableval=$enable_static_bin; enable_static=yes
+ enable_shared=no
+ enable_static_bin=yes
else
:
fi
@@ -2870,7 +2841,7 @@
# Define the identity of the package.
PACKAGE='iperf'
- VERSION='3.8'
+ VERSION='3.9'
cat >>confdefs.h <<_ACEOF
@@ -7465,16 +7436,11 @@
_lt_dar_allow_undefined='$wl-undefined ${wl}suppress' ;;
darwin1.*)
_lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;;
- darwin*) # darwin 5.x on
- # if running on 10.5 or later, the deployment target defaults
- # to the OS version, if on x86, and 10.4, the deployment
- # target defaults to 10.4. Don't you love it?
- case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in
- 10.0,*86*-darwin8*|10.0,*-darwin[91]*)
- _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;;
- 10.[012][,.]*)
+ darwin*)
+ case ${MACOSX_DEPLOYMENT_TARGET},$host in
+ 10.[012],*|,*powerpc*)
_lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;;
- 10.*)
+ *)
_lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;;
esac
;;
@@ -7824,6 +7790,36 @@
enable_win32_dll=no
+ # Check whether --enable-shared was given.
+if test "${enable_shared+set}" = set; then :
+ enableval=$enable_shared; p=${PACKAGE-default}
+ case $enableval in
+ yes) enable_shared=yes ;;
+ no) enable_shared=no ;;
+ *)
+ enable_shared=no
+ # Look at the argument we got. We use all the common list separators.
+ lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR,
+ for pkg in $enableval; do
+ IFS=$lt_save_ifs
+ if test "X$pkg" = "X$p"; then
+ enable_shared=yes
+ fi
+ done
+ IFS=$lt_save_ifs
+ ;;
+ esac
+else
+ enable_shared=yes
+fi
+
+
+
+
+
+
+
+
# Check whether --enable-static was given.
if test "${enable_static+set}" = set; then :
@@ -12946,9 +12942,7 @@
# Check if enable profiling
# Check whether --enable-profiling was given.
if test "${enable_profiling+set}" = set; then :
- enableval=$enable_profiling; enable_profiling=yes
-else
- :
+ enableval=$enable_profiling;
fi
if test x$enable_profiling = xyes; then
@@ -13415,7 +13409,7 @@
#define HAVE_NETINET_SCTP_H 1
_ACEOF
-$as_echo "#define HAVE_SCTP 1" >>confdefs.h
+$as_echo "#define HAVE_SCTP_H 1" >>confdefs.h
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing sctp_bindx" >&5
$as_echo_n "checking for library containing sctp_bindx... " >&6; }
@@ -14573,7 +14567,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 3.8, which was
+This file was extended by iperf $as_me 3.9, which was
generated by GNU Autoconf 2.69. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@@ -14640,7 +14634,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 3.8
+iperf config.status 3.9
configured by $0, generated by GNU Autoconf 2.69,
with options \\"\$ac_cs_config\\"
@@ -14769,9 +14763,9 @@
sed_quote_subst='$sed_quote_subst'
double_quote_subst='$double_quote_subst'
delay_variable_subst='$delay_variable_subst'
-enable_shared='`$ECHO "$enable_shared" | $SED "$delay_single_quote_subst"`'
macro_version='`$ECHO "$macro_version" | $SED "$delay_single_quote_subst"`'
macro_revision='`$ECHO "$macro_revision" | $SED "$delay_single_quote_subst"`'
+enable_shared='`$ECHO "$enable_shared" | $SED "$delay_single_quote_subst"`'
enable_static='`$ECHO "$enable_static" | $SED "$delay_single_quote_subst"`'
pic_mode='`$ECHO "$pic_mode" | $SED "$delay_single_quote_subst"`'
enable_fast_install='`$ECHO "$enable_fast_install" | $SED "$delay_single_quote_subst"`'
@@ -15803,13 +15797,13 @@
# ### BEGIN LIBTOOL CONFIG
-# Whether or not to build shared libraries.
-build_libtool_libs=$enable_shared
-
# Which release of libtool.m4 was used?
macro_version=$macro_version
macro_revision=$macro_revision
+# Whether or not to build shared libraries.
+build_libtool_libs=$enable_shared
+
# Whether or not to build static libraries.
build_old_libs=$enable_static
diff --git a/configure.ac b/configure.ac
index 25c44bb..939cf0a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -24,7 +24,7 @@
# file for complete information.
# Initialize the autoconf system for the specified tool, version and mailing list
-AC_INIT(iperf, 3.8, https://github.com/esnet/iperf, iperf, https://software.es.net/iperf/)
+AC_INIT(iperf, 3.9, https://github.com/esnet/iperf, iperf, https://software.es.net/iperf/)
m4_include([config/ax_check_openssl.m4])
m4_include([config/iperf_config_static_bin.m4])
AC_LANG(C)
@@ -58,9 +58,7 @@
# Check if enable profiling
AC_ARG_ENABLE([profiling],
- AS_HELP_STRING([--enable-profiling], [Enable iperf3 profiling binary]),
- [enable_profiling=yes],
- [:])
+ AS_HELP_STRING([--enable-profiling], [Enable iperf3 profiling binary]))
AM_CONDITIONAL([ENABLE_PROFILING], [test x$enable_profiling = xyes])
# Checks for header files.
@@ -119,7 +117,7 @@
if $try_sctp; then
AC_CHECK_HEADERS([sys/socket.h])
AC_CHECK_HEADERS([netinet/sctp.h],
- AC_DEFINE([HAVE_SCTP], [1], [Have SCTP support.])
+ AC_DEFINE([HAVE_SCTP_H], [1], [Have SCTP support.])
AC_SEARCH_LIBS(sctp_bindx, [sctp])
AC_CHECK_TYPES([struct sctp_assoc_value], [], [],
[[#include <netinet/sctp.h>]]),
diff --git a/docs/conf.py b/docs/conf.py
index 17aa556..083a3bf 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -52,10 +52,10 @@
# built documents.
#
# The short X.Y version.
-version = '3.7'
+version = '3.8.1'
# The full version, including alpha/beta/rc tags.
-release = '3.7'
+release = '3.8.1'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
diff --git a/docs/faq.rst b/docs/faq.rst
index c7170cc..d7d182e 100644
--- a/docs/faq.rst
+++ b/docs/faq.rst
@@ -65,7 +65,9 @@
generation of shared libraries and link the executable
statically. For iperf-3.8 or later, configuring as ``configure
--enable-static-bin`` is another, shorter way to accomplish
- this.
+ this. If SCTP is installed on the system it might also be
+ necessary to pass the ``--without-sctp`` flag at configure
+ time.
#. Compile as normal.
@@ -74,10 +76,16 @@
How can I build on a system that doesn't support profiled executables?
This problem has been noted by users attempting to build iperf3 for
- Android systems. There are several workarounds. In order from least
+ Android systems, as well as some recent versions of macOS.
+ There are several workarounds. In order from least
effort to most effort:
- #. Beginning with iperf-3.6, the ``--disable-profiling`` flag can be
+ #. Beginning with iperf-3.8, profiled executables are actually not
+ built by default, so this question becomes somewhat moot. Pass
+ the ``--enable-profiling`` flag to ``configure`` to build
+ profiled executables.
+
+ #. In iperf-3.6 and iperf-3.7, the ``--disable-profiling`` flag can be
passed to ``configure`` to disable the building of profiled
object files and the profiled executable.
@@ -119,6 +127,59 @@
iperf3 UDP does not seem to work at bandwidths less than 100Kbps. Why?
You'll need to reduce the default packet length to get UDP rates of less that 100Kbps. Try ``-l100``.
+TCP throughput drops to (almost) zero during a test, what's going on?
+ A drop in throughput to almost zero, except maybe for the first
+ reported interval(s), may be related to problems in NIC TCP Offload,
+ which is used to offload TCP functionality to the NIC (see
+ https://en.wikipedia.org/wiki/TCP_offload_engine). The goal of TCP
+ Offload is to save main CPU performance, mainly in the areas of
+ segmentation and reassembly of large packets and checksum
+ computation.
+
+ When TCP packets are sent with the "Don't Fragment" flag set, which
+ is the recommended setting, segmentation is done by the TCP stack
+ based on the reported next hop MSS in the ICMP Fragmentation Needed
+ message. With TCP Offload, active segmentation is done by the NIC on
+ the sending side, which is known as TCP Segmentation offload (TSO)
+ or in Windows as Large Send Offload (LSO). It seems that there are
+ TSO/LSO implementations which for some reason ignore the reported
+ MSS and therefore don’t perform segmentation. In these cases, when
+ large packets are sent, e.g. the default iperf3 128KB (131,072
+ bytes), iperf3 will show that data was sent in the first interval,
+ but since the packets don’t get to the server, no ack is received
+ and therefore no data is sent in the following intervals. It may
+ happen that after certain timeout the main CPU will re-send the
+ packet by re-segmenting it, and in these cases data will get to the
+ server after a while. However, it seems that segmentation is not
+ automatically continued with the next packet, so the data transfer
+ rate be very low.
+
+ The recommended solution in such a case is to disable TSO/LSO, at
+ least on the relevant port. See for example:
+ https://atomicit.ca/kb/articles/slow-network-speed-windows-10/. If
+ that doesn’t help then "Don't Fragment" TCP flag may be
+ disabled. See for example:
+ https://support.microsoft.com/en-us/help/900926/recommended-tcp-ip-settings-for-wan-links-with-a-mtu-size-of-less-than. However,
+ note that disabling the “Don’t Fragment” flag may cause other
+ issues.
+
+ To test whether TSO/LSO may be the problem, do the following:
+
+ * If different machine configurations are used for the client and
+ server, try the iperf3 reverse mode (``-R``). If TSO/LSO is only
+ enabled on the client machine, this test should succeed.
+ * Reduce the sending length to a small value that should not require
+ segmentation, using the iperf3 ``-l`` option, e.g. ``-l 512``. It
+ may also help to reduce the MTU by using the iperf3 ``-M`` option,
+ e.g. ``-M 1460``.
+ * Using tools like Wireshark, identify the required MSS in the ICMP
+ Fragmentation Needed messages (if reported). Run tests with the
+ ``-l`` value set to 2 times the MSS and then 4 times, 6 times,
+ etc. With TSO/LSO issue in each test the throughput should be
+ reduced more. It may help to increase the testing time beyond the
+ default 10 seconds to better see the behavior (iperf3 ``-t``
+ option).
+
What congestion control algorithms are supported?
On Linux, run this command to see the available congestion control
algorithms (note that some algorithms are packaged as kernel
diff --git a/docs/news.rst b/docs/news.rst
index 4104d36..72c0d93 100644
--- a/docs/news.rst
+++ b/docs/news.rst
@@ -1,13 +1,31 @@
iperf3 Project News
===================
+2020-06-10: iperf-3.8.1 released
+---------------------------------
+
+| URL: https://downloads.es.net/pub/iperf/iperf-3.8.1.tar.gz
+| SHA256: ``e5b080f3273a8a715a4100f13826ac2ca31cc7b1315925631b2ecf64957ded96 iperf-3.8.1.tar.gz``
+
+iperf 3.8.1 fixes a regression with ``make install`` in iperf 3.8. It
+is otherwise identical to iperf 3.8.
+
+2020-06-08: iperf-3.8 released
+-------------------------------
+
+| URL: https://downloads.es.net/pub/iperf/iperf-3.8.tar.gz
+| SHA256: ``edc1c317b0ae31925e5eb84f0295faefbaa1db3229f4693e11d954d114de4bcd iperf-3.8.tar.gz``
+
+iperf 3.8 contains minor bugfixes and enhancements.
+
+
2019-06-21: iperf-3.7 released
-------------------------------
| URL: https://downloads.es.net/pub/iperf/iperf-3.7.tar.gz
| SHA256: ``d846040224317caf2f75c843d309a950a7db23f9b44b94688ccbe557d6d1710c iperf-3.7.tar.gz``
-iperf 3.6 adds the ``--bidir`` flag for bidirectional tests, includes
+iperf 3.7 adds the ``--bidir`` flag for bidirectional tests, includes
some minor enhancements, and fixes a number of bugs. More details can
be found in the release notes.
diff --git a/src/iperf.h b/src/iperf.h
index 6ce77f5..5b1da46 100644
--- a/src/iperf.h
+++ b/src/iperf.h
@@ -1,5 +1,5 @@
/*
- * iperf, Copyright (c) 2014-2019, The Regents of the University of
+ * iperf, Copyright (c) 2014-2020, The Regents of the University of
* California, through Lawrence Berkeley National Laboratory (subject
* to receipt of any required approvals from the U.S. Dept. of
* Energy). All rights reserved.
@@ -68,7 +68,9 @@
#include <openssl/evp.h>
#endif // HAVE_SSL
+#if !defined(__IPERF_API_H)
typedef uint64_t iperf_size_t;
+#endif // __IPERF_API_H
struct iperf_interval_results
{
@@ -135,7 +137,10 @@
int domain; /* AF_INET or AF_INET6 */
int socket_bufsize; /* window size for TCP */
int blksize; /* size of read/writes (-l) */
- uint64_t rate; /* target data rate for application pacing*/
+ iperf_size_t rate; /* target data rate for application pacing*/
+ iperf_size_t bitrate_limit; /* server's maximum allowed total data rate for all streams*/
+ double bitrate_limit_interval; /* interval for avaraging total data rate */
+ int bitrate_limit_stats_per_interval; /* calculated number of stats periods for averaging total data rate */
uint64_t fqrate; /* target data rate for FQ pacing*/
int pacing_timer; /* pacing timer in microseconds */
int burst; /* packets per burst */
@@ -298,6 +303,8 @@
int forceflush; /* --forceflush - flushing output at every interval */
int multisend;
int repeating_payload; /* --repeating-payload */
+ int timestamps; /* --timestamps */
+ char *timestamp_format;
char *json_output_string; /* rendered JSON output if json_output is set */
/* Select related parameters */
@@ -328,6 +335,11 @@
iperf_size_t bytes_received;
iperf_size_t blocks_received;
+ iperf_size_t bitrate_limit_stats_count; /* Number of stats periods accumulated for server's total bitrate average */
+ iperf_size_t *bitrate_limit_intervals_traffic_bytes; /* Pointer to a cyclic array that includes the last interval's bytes transferred */
+ iperf_size_t bitrate_limit_last_interval_index; /* Index of the last interval traffic insrted into the cyclic array */
+ int bitrate_limit_exceeded; /* Set by callback routine when average data rate exceeded the server's bitrate limit */
+
char cookie[COOKIE_SIZE];
// struct iperf_stream *streams; /* pointer to list of struct stream */
SLIST_HEAD(slisthead, iperf_stream) streams;
@@ -385,6 +397,8 @@
#define MAX_MSS (9 * 1024)
#define MAX_STREAMS 128
+#define TIMESTAMP_FORMAT "%c "
+
extern int gerror; /* error value from getaddrinfo(3), for use in internal error handling */
#endif /* !__IPERF_H */
diff --git a/src/iperf3.1 b/src/iperf3.1
index 93fe7b9..97d66ed 100644
--- a/src/iperf3.1
+++ b/src/iperf3.1
@@ -1,4 +1,4 @@
-.TH IPERF3 1 "June 2019" ESnet "User Manuals"
+.TH IPERF3 1 "July 2020" ESnet "User Manuals"
.SH NAME
iperf3 \- perform network throughput tests
.SH SYNOPSIS
@@ -153,6 +153,14 @@
force flushing output at every interval.
Used to avoid buffering when sending output to pipe.
.TP
+.BR --timestamps " [\fIformat\fR]"
+prepend a timestamp at the start of each output line.
+By default, timestamps have the format emitted by
+.BR ctime ( 1 ).
+Optionally, a format specification can be passed to customize the
+timestamps, see
+.BR strftime ( 3 ).
+.TP
.BR -d ", " --debug " "
emit debugging output.
Primarily (perhaps exclusively) of use to developers.
@@ -177,6 +185,15 @@
.BR -1 ", " --one-off
handle one client connection, then exit.
.TP
+.BR --server-bitrate-limit " \fIn\fR[KMGT]"
+set a limit on the server side, which will cause a test to abort if
+the client specifies a test of more than \fIn\fR bits per second, or
+if the average data sent or received by the client (including all data
+streams) is greater than \fIn\fR bits per second. The default limit
+is zero, which implies no limit. The interval over which to average
+the data rate is 5 seconds by default, but can be specified by adding
+a '/' and a number to the bitrate specifier.
+.TP
.BR --rsa-private-key-path " \fIfile\fR"
path to the RSA private key (not password-protected) used to decrypt
authentication credentials from the client (if built with OpenSSL
@@ -209,7 +226,7 @@
Providing a shorter value may speed up detection of a down iperf3
server.
.TP
-.BR -b ", " --bitrate " \fIn\fR[KM]"
+.BR -b ", " --bitrate " \fIn\fR[KMGT]"
set target bitrate to \fIn\fR bits/sec (default 1 Mbit/sec for UDP,
unlimited for TCP/SCTP).
If there are multiple streams (\-P flag), the throughput limit is applied
@@ -226,7 +243,7 @@
This option replaces the \--bandwidth flag, which is now deprecated
but (at least for now) still accepted.
.TP
-.BR --pacing-timer " \fIn\fR[KMG]"
+.BR --pacing-timer " \fIn\fR[KMGT]"
set pacing timer interval in microseconds (default 1000 microseconds,
or 1 ms).
This controls iperf3's internal pacing timer for the \-b/\--bitrate
@@ -236,7 +253,7 @@
emitted by iperf3, but potentially at the cost of performance due to
more frequent timer processing.
.TP
-.BR --fq-rate " \fIn\fR[KM]"
+.BR --fq-rate " \fIn\fR[KMGT]"
Set a rate to be used with fair-queueing based socket-level pacing,
in bits per second.
This pacing (if specified) will be in addition to any pacing due to
@@ -253,13 +270,13 @@
.BR -t ", " --time " \fIn\fR"
time in seconds to transmit for (default 10 secs)
.TP
-.BR -n ", " --bytes " \fIn\fR[KM]"
+.BR -n ", " --bytes " \fIn\fR[KMGT]"
number of bytes to transmit (instead of \-t)
.TP
-.BR -k ", " --blockcount " \fIn\fR[KM]"
+.BR -k ", " --blockcount " \fIn\fR[KMGT]"
number of blocks (packets) to transmit (instead of \-t or \-n)
.TP
-.BR -l ", " --length " \fIn\fR[KM]"
+.BR -l ", " --length " \fIn\fR[KMGT]"
length of buffer to read or write. For TCP tests, the default value
is 128KB.
In the case of UDP, iperf3 tries to dynamically determine a reasonable
@@ -282,7 +299,7 @@
test in both directions (normal and reverse), with both the client and
server sending and receiving data simultaneously
.TP
-.BR -w ", " --window " \fIn\fR[KM]"
+.BR -w ", " --window " \fIn\fR[KMGT]"
window size / socket buffer size (this gets sent to the server and used on that side too)
.TP
.BR -M ", " --set-mss " \fIn\fR"
diff --git a/src/iperf_api.c b/src/iperf_api.c
index ed643b7..88fed30 100644
--- a/src/iperf_api.c
+++ b/src/iperf_api.c
@@ -57,6 +57,7 @@
#include <sched.h>
#include <setjmp.h>
#include <stdarg.h>
+#include <math.h>
#if defined(HAVE_CPUSET_SETAFFINITY)
#include <sys/param.h>
@@ -76,9 +77,9 @@
#include "iperf_api.h"
#include "iperf_udp.h"
#include "iperf_tcp.h"
-#if defined(HAVE_SCTP)
+#if defined(HAVE_SCTP_H)
#include "iperf_sctp.h"
-#endif /* HAVE_SCTP */
+#endif /* HAVE_SCTP_H */
#include "timer.h"
#include "cjson.h"
@@ -119,7 +120,7 @@
}
-void warning(char *str)
+void warning(const char *str)
{
fprintf(stderr, "warning: %s\n", str);
}
@@ -164,6 +165,24 @@
}
uint64_t
+iperf_get_test_bitrate_limit(struct iperf_test *ipt)
+{
+ return ipt->settings->bitrate_limit;
+}
+
+double
+iperf_get_test_bitrate_limit_interval(struct iperf_test *ipt)
+{
+ return ipt->settings->bitrate_limit_interval;
+}
+
+int
+iperf_get_test_bitrate_limit_stats_per_interval(struct iperf_test *ipt)
+{
+ return ipt->settings->bitrate_limit_stats_per_interval;
+}
+
+uint64_t
iperf_get_test_fqrate(struct iperf_test *ipt)
{
return ipt->settings->fqrate;
@@ -242,6 +261,18 @@
}
int
+iperf_get_test_timestamps(struct iperf_test *ipt)
+{
+ return ipt->timestamps;
+}
+
+const char *
+iperf_get_test_timestamp_format(struct iperf_test *ipt)
+{
+ return ipt->timestamp_format;
+}
+
+int
iperf_get_test_repeating_payload(struct iperf_test *ipt)
{
return ipt->repeating_payload;
@@ -401,7 +432,7 @@
}
void
-iperf_set_test_logfile(struct iperf_test *ipt, char *logfile)
+iperf_set_test_logfile(struct iperf_test *ipt, const char *logfile)
{
ipt->logfile = strdup(logfile);
}
@@ -413,6 +444,24 @@
}
void
+iperf_set_test_bitrate_limit_maximum(struct iperf_test *ipt, uint64_t total_rate)
+{
+ ipt->settings->bitrate_limit = total_rate;
+}
+
+void
+iperf_set_test_bitrate_limit_interval(struct iperf_test *ipt, uint64_t bitrate_limit_interval)
+{
+ ipt->settings->bitrate_limit_interval = bitrate_limit_interval;
+}
+
+void
+iperf_set_test_bitrate_limit_stats_per_interval(struct iperf_test *ipt, uint64_t bitrate_limit_stats_per_interval)
+{
+ ipt->settings->bitrate_limit_stats_per_interval = bitrate_limit_stats_per_interval;
+}
+
+void
iperf_set_test_fqrate(struct iperf_test *ipt, uint64_t fqrate)
{
ipt->settings->fqrate = fqrate;
@@ -466,6 +515,18 @@
ipt->repeating_payload = repeating_payload;
}
+void
+iperf_set_test_timestamps(struct iperf_test *ipt, int timestamps)
+{
+ ipt->timestamps = timestamps;
+}
+
+void
+iperf_set_test_timestamp_format(struct iperf_test *ipt, const char *tf)
+{
+ ipt->timestamp_format = strdup(tf);
+}
+
static void
check_sender_has_retransmits(struct iperf_test *ipt)
{
@@ -496,13 +557,13 @@
}
void
-iperf_set_test_server_hostname(struct iperf_test *ipt, char *server_hostname)
+iperf_set_test_server_hostname(struct iperf_test *ipt, const char *server_hostname)
{
ipt->server_hostname = strdup(server_hostname);
}
void
-iperf_set_test_template(struct iperf_test *ipt, char *tmp_template)
+iperf_set_test_template(struct iperf_test *ipt, const char *tmp_template)
{
ipt->tmp_template = strdup(tmp_template);
}
@@ -557,38 +618,38 @@
#if defined(HAVE_SSL)
void
-iperf_set_test_client_username(struct iperf_test *ipt, char *client_username)
+iperf_set_test_client_username(struct iperf_test *ipt, const char *client_username)
{
ipt->settings->client_username = strdup(client_username);
}
void
-iperf_set_test_client_password(struct iperf_test *ipt, char *client_password)
+iperf_set_test_client_password(struct iperf_test *ipt, const char *client_password)
{
ipt->settings->client_password = strdup(client_password);
}
void
-iperf_set_test_client_rsa_pubkey(struct iperf_test *ipt, char *client_rsa_pubkey_base64)
+iperf_set_test_client_rsa_pubkey(struct iperf_test *ipt, const char *client_rsa_pubkey_base64)
{
ipt->settings->client_rsa_pubkey = load_pubkey_from_base64(client_rsa_pubkey_base64);
}
void
-iperf_set_test_server_authorized_users(struct iperf_test *ipt, char *server_authorized_users)
+iperf_set_test_server_authorized_users(struct iperf_test *ipt, const char *server_authorized_users)
{
ipt->server_authorized_users = strdup(server_authorized_users);
}
void
-iperf_set_test_server_rsa_privkey(struct iperf_test *ipt, char *server_rsa_privkey_base64)
+iperf_set_test_server_rsa_privkey(struct iperf_test *ipt, const char *server_rsa_privkey_base64)
{
ipt->server_rsa_private_key = load_privkey_from_base64(server_rsa_privkey_base64);
}
#endif // HAVE_SSL
void
-iperf_set_test_bind_address(struct iperf_test *ipt, char *bnd_address)
+iperf_set_test_bind_address(struct iperf_test *ipt, const char *bnd_address)
{
ipt->bind_address = strdup(bnd_address);
}
@@ -612,7 +673,7 @@
}
void
-iperf_set_test_extra_data(struct iperf_test *ipt, char *dat)
+iperf_set_test_extra_data(struct iperf_test *ipt, const char *dat)
{
ipt->extra_data = strdup(dat);
}
@@ -816,6 +877,7 @@
{"udp", no_argument, NULL, 'u'},
{"bitrate", required_argument, NULL, 'b'},
{"bandwidth", required_argument, NULL, 'b'},
+ {"server-bitrate-limit", required_argument, NULL, OPT_SERVER_BITRATE_LIMIT},
{"time", required_argument, NULL, 't'},
{"bytes", required_argument, NULL, 'n'},
{"blockcount", required_argument, NULL, 'k'},
@@ -840,6 +902,7 @@
{"omit", required_argument, NULL, 'O'},
{"file", required_argument, NULL, 'F'},
{"repeating-payload", no_argument, NULL, OPT_REPEATING_PAYLOAD},
+ {"timestamps", optional_argument, NULL, OPT_TIMESTAMPS},
#if defined(HAVE_CPU_AFFINITY)
{"affinity", required_argument, NULL, 'A'},
#endif /* HAVE_CPU_AFFINITY */
@@ -848,7 +911,7 @@
{"congestion", required_argument, NULL, 'C'},
{"linux-congestion", required_argument, NULL, 'C'},
#endif /* HAVE_TCP_CONGESTION */
-#if defined(HAVE_SCTP)
+#if defined(HAVE_SCTP_H)
{"sctp", no_argument, NULL, OPT_SCTP},
{"nstreams", required_argument, NULL, OPT_NUMSTREAMS},
{"xbind", required_argument, NULL, 'X'},
@@ -968,14 +1031,14 @@
client_flag = 1;
break;
case OPT_SCTP:
-#if defined(HAVE_SCTP)
+#if defined(HAVE_SCTP_H)
set_protocol(test, Psctp);
client_flag = 1;
break;
-#else /* HAVE_SCTP */
+#else /* HAVE_SCTP_H */
i_errno = IEUNIMP;
return -1;
-#endif /* HAVE_SCTP */
+#endif /* HAVE_SCTP_H */
case OPT_NUMSTREAMS:
#if defined(linux) || defined(__FreeBSD__)
@@ -1001,6 +1064,21 @@
rate_flag = 1;
client_flag = 1;
break;
+ case OPT_SERVER_BITRATE_LIMIT:
+ slash = strchr(optarg, '/');
+ if (slash) {
+ *slash = '\0';
+ ++slash;
+ test->settings->bitrate_limit_interval = atof(slash);
+ if (test->settings->bitrate_limit_interval != 0 && /* Using same Max/Min limits as for Stats Interval */
+ (test->settings->bitrate_limit_interval < MIN_INTERVAL || test->settings->bitrate_limit_interval > MAX_INTERVAL) ) {
+ i_errno = IETOTALINTERVAL;
+ return -1;
+ }
+ }
+ test->settings->bitrate_limit = unit_atof_rate(optarg);
+ server_flag = 1;
+ break;
case 't':
test->duration = atoi(optarg);
if (test->duration > MAX_TIME) {
@@ -1149,6 +1227,15 @@
test->repeating_payload = 1;
client_flag = 1;
break;
+ case OPT_TIMESTAMPS:
+ iperf_set_test_timestamps(test, 1);
+ if (optarg) {
+ iperf_set_test_timestamp_format(test, optarg);
+ }
+ else {
+ iperf_set_test_timestamp_format(test, TIMESTAMP_FORMAT);
+ }
+ break;
case 'O':
test->omit = atoi(optarg);
if (test->omit < 0 || test->omit > 60) {
@@ -1292,6 +1379,7 @@
if ((client_password = getenv("IPERF3_PASSWORD")) != NULL)
client_password = strdup(client_password);
else if (iperf_getpass(&client_password, &s, stdin) < 0){
+ i_errno = IESETCLIENTAUTH;
return -1;
}
if (test_load_pubkey_from_file(client_rsa_public_key) < 0){
@@ -1374,6 +1462,13 @@
return -1;
}
+ /* Set Total-rate average interval to multiplicity of State interval */
+ if (test->settings->bitrate_limit_interval != 0) {
+ test->settings->bitrate_limit_stats_per_interval =
+ (test->settings->bitrate_limit_interval <= test->stats_interval ?
+ 1 : round(test->settings->bitrate_limit_interval/test->stats_interval) );
+ }
+
/* Show warning if JSON output is used with explicit report format */
if ((test->json_output) && (test->settings->unit_format != 'a')) {
warning("Report format (-f) flag ignored with JSON output (-J)");
@@ -1436,6 +1531,45 @@
}
}
+/* Verify that average traffic is not greater than the specifid limit */
+void
+iperf_check_total_rate(struct iperf_test *test, iperf_size_t last_interval_bytes_transferred)
+{
+ double seconds;
+ uint64_t bits_per_second;
+ iperf_size_t total_bytes;
+ int i;
+
+ if (test->done || test->settings->bitrate_limit == 0) // Continue only if check should be done
+ return;
+
+ /* Add last inetrval's transffered bytes to the array */
+ if (++test->bitrate_limit_last_interval_index >= test->settings->bitrate_limit_stats_per_interval)
+ test->bitrate_limit_last_interval_index = 0;
+ test->bitrate_limit_intervals_traffic_bytes[test->bitrate_limit_last_interval_index] = last_interval_bytes_transferred;
+
+ /* Ensure that enough stats periods passed to allow averaging throughput */
+ test->bitrate_limit_stats_count += 1;
+ if (test->bitrate_limit_stats_count < test->settings->bitrate_limit_stats_per_interval)
+ return;
+
+ /* Calculating total bytes traffic to be averaged */
+ for (total_bytes = 0, i = 0; i < test->settings->bitrate_limit_stats_per_interval; i++) {
+ total_bytes += test->bitrate_limit_intervals_traffic_bytes[i];
+ }
+
+ seconds = test->stats_interval * test->settings->bitrate_limit_stats_per_interval;
+ bits_per_second = total_bytes * 8 / seconds;
+ if (test->debug) {
+ iperf_printf(test,"Interval %" PRIu64 " - throughput %" PRIu64 " bps (limit %" PRIu64 ")\n", test->bitrate_limit_stats_count, bits_per_second, test->settings->bitrate_limit);
+ }
+
+ if (bits_per_second > test->settings->bitrate_limit) {
+ iperf_err(test, "Total throughput of %" PRIu64 " bps exceeded %" PRIu64 " bps limit", bits_per_second, test->settings->bitrate_limit);
+ test->bitrate_limit_exceeded = 1;
+ }
+}
+
int
iperf_send(struct iperf_test *test, fd_set *write_setP)
{
@@ -1480,7 +1614,8 @@
if (test->settings->burst != 0) {
iperf_time_now(&now);
SLIST_FOREACH(sp, &test->streams, streams)
- iperf_check_throttle(sp, &now);
+ if (sp->sender)
+ iperf_check_throttle(sp, &now);
}
if (write_setP != NULL)
SLIST_FOREACH(sp, &test->streams, streams)
@@ -1562,7 +1697,7 @@
}
SLIST_FOREACH(sp, &test->streams, streams) {
sp->green_light = 1;
- if (test->settings->rate != 0) {
+ if (test->settings->rate != 0 && sp->sender) {
cd.p = sp;
sp->send_timer = tmr_create(NULL, send_timer_proc, cd, test->settings->pacing_timer, 1);
if (sp->send_timer == NULL) {
@@ -1654,6 +1789,7 @@
}
return -1;
}
+
FD_SET(s, &test->read_set);
test->max_fd = (s > test->max_fd) ? s : test->max_fd;
test->prot_listener = s;
@@ -2289,6 +2425,14 @@
}
memset(test->settings, 0, sizeof(struct iperf_settings));
+ test->bitrate_limit_intervals_traffic_bytes = (iperf_size_t *) malloc(sizeof(iperf_size_t) * MAX_INTERVAL);
+ if (!test->bitrate_limit_intervals_traffic_bytes) {
+ free(test);
+ i_errno = IENEWTEST;
+ return NULL;
+ }
+ memset(test->bitrate_limit_intervals_traffic_bytes, 0, sizeof(sizeof(iperf_size_t) * MAX_INTERVAL));
+
/* By default all output goes to stdout */
test->outfile = stdout;
@@ -2322,9 +2466,9 @@
iperf_defaults(struct iperf_test *testp)
{
struct protocol *tcp, *udp;
-#if defined(HAVE_SCTP)
+#if defined(HAVE_SCTP_H)
struct protocol *sctp;
-#endif /* HAVE_SCTP */
+#endif /* HAVE_SCTP_H */
testp->omit = OMIT;
testp->duration = DURATION;
@@ -2356,6 +2500,9 @@
testp->settings->socket_bufsize = 0; /* use autotuning */
testp->settings->blksize = DEFAULT_TCP_BLKSIZE;
testp->settings->rate = 0;
+ testp->settings->bitrate_limit = 0;
+ testp->settings->bitrate_limit_interval = 5;
+ testp->settings->bitrate_limit_stats_per_interval = 0;
testp->settings->fqrate = 0;
testp->settings->pacing_timer = 1000;
testp->settings->burst = 0;
@@ -2403,7 +2550,7 @@
set_protocol(testp, Ptcp);
-#if defined(HAVE_SCTP)
+#if defined(HAVE_SCTP_H)
sctp = protocol_new();
if (!sctp) {
protocol_free(tcp);
@@ -2421,7 +2568,7 @@
sctp->init = iperf_sctp_init;
SLIST_INSERT_AFTER(udp, sctp, protocols);
-#endif /* HAVE_SCTP */
+#endif /* HAVE_SCTP_H */
testp->on_new_stream = iperf_on_new_stream;
testp->on_test_start = iperf_on_test_start;
@@ -2497,6 +2644,8 @@
free(test->congestion_used);
if (test->remote_congestion_used)
free(test->remote_congestion_used);
+ if (test->timestamp_format)
+ free(test->timestamp_format);
if (test->omit_timer != NULL)
tmr_cancel(test->omit_timer);
if (test->timer != NULL)
@@ -2513,6 +2662,15 @@
free(prot);
}
+ if (test->logfile) {
+ free(test->logfile);
+ test->logfile = NULL;
+ if (test->outfile) {
+ fclose(test->outfile);
+ test->outfile = NULL;
+ }
+ }
+
if (test->server_output_text) {
free(test->server_output_text);
test->server_output_text = NULL;
@@ -2544,6 +2702,10 @@
}
}
+ /* Free interval's traffic array for avrage rate calculations */
+ if (test->bitrate_limit_intervals_traffic_bytes != NULL)
+ free(test->bitrate_limit_intervals_traffic_bytes);
+
/* XXX: Why are we setting these values to NULL? */
// test->streams = NULL;
test->stats_callback = NULL;
@@ -2556,6 +2718,7 @@
iperf_reset_test(struct iperf_test *test)
{
struct iperf_stream *sp;
+ int i;
/* Free streams */
while (!SLIST_EMPTY(&test->streams)) {
@@ -2609,6 +2772,13 @@
test->other_side_has_retransmits = 0;
+ test->bitrate_limit_stats_count = 0;
+ test->bitrate_limit_last_interval_index = 0;
+ test->bitrate_limit_exceeded = 0;
+
+ for (i = 0; i < MAX_INTERVAL; i++)
+ test->bitrate_limit_intervals_traffic_bytes[i] = 0;
+
test->reverse = 0;
test->bidirectional = 0;
test->no_delay = 0;
@@ -2712,12 +2882,16 @@
struct iperf_stream_result *rp = NULL;
struct iperf_interval_results *irp, temp;
struct iperf_time temp_time;
+ iperf_size_t total_interval_bytes_transferred = 0;
temp.omitted = test->omitting;
SLIST_FOREACH(sp, &test->streams, streams) {
rp = sp->result;
temp.bytes_transferred = sp->sender ? rp->bytes_sent_this_interval : rp->bytes_received_this_interval;
-
+
+ // Total bytes transferred this interval
+ total_interval_bytes_transferred += rp->bytes_sent_this_interval + rp->bytes_received_this_interval;
+
irp = TAILQ_LAST(&rp->interval_results, irlisthead);
/* result->end_time contains timestamp of previous interval */
if ( irp != NULL ) /* not the 1st interval */
@@ -2776,6 +2950,11 @@
add_to_interval_list(rp, &temp);
rp->bytes_sent_this_interval = rp->bytes_received_this_interval = 0;
}
+
+ /* Verify that total server's throughput is not above specified limit */
+ if (test->role == 's') {
+ iperf_check_total_rate(test, total_interval_bytes_transferred);
+ }
}
/**
@@ -4135,11 +4314,24 @@
#endif /* neither HAVE_SCHED_SETAFFINITY nor HAVE_CPUSET_SETAFFINITY nor HAVE_SETPROCESSAFFINITYMASK */
}
+char iperf_timestr[100];
+
int
iperf_printf(struct iperf_test *test, const char* format, ...)
{
va_list argp;
int r = -1;
+ time_t now;
+ struct tm *ltm = NULL;
+ char *ct = NULL;
+
+ /* Timestamp if requested */
+ if (iperf_get_test_timestamps(test)) {
+ time(&now);
+ ltm = localtime(&now);
+ strftime(iperf_timestr, sizeof(iperf_timestr), iperf_get_test_timestamp_format(test), ltm);
+ ct = iperf_timestr;
+ }
/*
* There are roughly two use cases here. If we're the client,
@@ -4154,6 +4346,9 @@
* to be buffered up anyway.
*/
if (test->role == 'c') {
+ if (ct) {
+ fprintf(test->outfile, "%s", ct);
+ }
if (test->title)
fprintf(test->outfile, "%s: ", test->title);
va_start(argp, format);
@@ -4162,8 +4357,12 @@
}
else if (test->role == 's') {
char linebuffer[1024];
+ int i = 0;
+ if (ct) {
+ i = sprintf(linebuffer, "%s", ct);
+ }
va_start(argp, format);
- r = vsnprintf(linebuffer, sizeof(linebuffer), format, argp);
+ r = vsnprintf(linebuffer + i, sizeof(linebuffer), format, argp);
va_end(argp);
fprintf(test->outfile, "%s", linebuffer);
diff --git a/src/iperf_api.h b/src/iperf_api.h
index 3770b37..a51b773 100644
--- a/src/iperf_api.h
+++ b/src/iperf_api.h
@@ -45,6 +45,10 @@
struct iperf_stream;
struct iperf_time;
+#if !defined(__IPERF_H)
+typedef uint64_t iperf_size_t;
+#endif // __IPERF_H
+
/* default settings */
#define Ptcp SOCK_STREAM
#define Pudp SOCK_DGRAM
@@ -73,6 +77,8 @@
#define OPT_REPEATING_PAYLOAD 18
#define OPT_EXTRA_DATA 19
#define OPT_BIDIRECTIONAL 20
+#define OPT_SERVER_BITRATE_LIMIT 21
+#define OPT_TIMESTAMPS 22
/* states */
#define TEST_START 1
@@ -113,6 +119,8 @@
double iperf_get_test_stats_interval( struct iperf_test* ipt );
int iperf_get_test_num_streams( struct iperf_test* ipt );
int iperf_get_test_repeating_payload( struct iperf_test* ipt );
+int iperf_get_test_timestamps( struct iperf_test* ipt );
+const char* iperf_get_test_timestamp_format( struct iperf_test* ipt );
int iperf_get_test_server_port( struct iperf_test* ipt );
char* iperf_get_test_server_hostname( struct iperf_test* ipt );
char* iperf_get_test_template( struct iperf_test* ipt );
@@ -139,7 +147,7 @@
void iperf_set_test_stats_interval( struct iperf_test* ipt, double stats_interval );
void iperf_set_test_state( struct iperf_test* ipt, signed char state );
void iperf_set_test_blksize( struct iperf_test* ipt, int blksize );
-void iperf_set_test_logfile( struct iperf_test* ipt, char *logfile );
+void iperf_set_test_logfile( struct iperf_test* ipt, const char *logfile );
void iperf_set_test_rate( struct iperf_test* ipt, uint64_t rate );
void iperf_set_test_pacing_timer( struct iperf_test* ipt, int pacing_timer );
void iperf_set_test_bytes( struct iperf_test* ipt, uint64_t bytes );
@@ -149,28 +157,30 @@
void iperf_set_test_socket_bufsize( struct iperf_test* ipt, int socket_bufsize );
void iperf_set_test_num_streams( struct iperf_test* ipt, int num_streams );
void iperf_set_test_repeating_payload( struct iperf_test* ipt, int repeating_payload );
+void iperf_set_test_timestamps( struct iperf_test* ipt, int timestamps );
+void iperf_set_test_timestamp_format( struct iperf_test*, const char *tf );
void iperf_set_test_role( struct iperf_test* ipt, char role );
-void iperf_set_test_server_hostname( struct iperf_test* ipt, char* server_hostname );
-void iperf_set_test_template( struct iperf_test *ipt, char *tmp_template );
+void iperf_set_test_server_hostname( struct iperf_test* ipt, const char* server_hostname );
+void iperf_set_test_template( struct iperf_test *ipt, const char *tmp_template );
void iperf_set_test_reverse( struct iperf_test* ipt, int reverse );
void iperf_set_test_json_output( struct iperf_test* ipt, int json_output );
int iperf_has_zerocopy( void );
void iperf_set_test_zerocopy( struct iperf_test* ipt, int zerocopy );
void iperf_set_test_get_server_output( struct iperf_test* ipt, int get_server_output );
-void iperf_set_test_bind_address( struct iperf_test* ipt, char *bind_address );
+void iperf_set_test_bind_address( struct iperf_test* ipt, const char *bind_address );
void iperf_set_test_udp_counters_64bit( struct iperf_test* ipt, int udp_counters_64bit );
void iperf_set_test_one_off( struct iperf_test* ipt, int one_off );
void iperf_set_test_tos( struct iperf_test* ipt, int tos );
-void iperf_set_test_extra_data( struct iperf_test* ipt, char *dat );
+void iperf_set_test_extra_data( struct iperf_test* ipt, const char *dat );
void iperf_set_test_bidirectional( struct iperf_test* ipt, int bidirectional);
void iperf_set_test_no_delay( struct iperf_test* ipt, int no_delay);
#if defined(HAVE_SSL)
-void iperf_set_test_client_username(struct iperf_test *ipt, char *client_username);
-void iperf_set_test_client_password(struct iperf_test *ipt, char *client_password);
-void iperf_set_test_client_rsa_pubkey(struct iperf_test *ipt, char *client_rsa_pubkey_base64);
-void iperf_set_test_server_authorized_users(struct iperf_test *ipt, char *server_authorized_users);
-void iperf_set_test_server_rsa_privkey(struct iperf_test *ipt, char *server_rsa_privkey_base64);
+void iperf_set_test_client_username(struct iperf_test *ipt, const char *client_username);
+void iperf_set_test_client_password(struct iperf_test *ipt, const char *client_password);
+void iperf_set_test_client_rsa_pubkey(struct iperf_test *ipt, const char *client_rsa_pubkey_base64);
+void iperf_set_test_server_authorized_users(struct iperf_test *ipt, const char *server_authorized_users);
+void iperf_set_test_server_rsa_privkey(struct iperf_test *ipt, const char *server_rsa_privkey_base64);
#endif // HAVE_SSL
void iperf_set_test_connect_timeout(struct iperf_test *ipt, int ct);
@@ -268,7 +278,7 @@
void iperf_got_sigend(struct iperf_test *test) __attribute__ ((noreturn));
void usage(void);
void usage_long(FILE * f);
-void warning(char *);
+void warning(const char *);
int iperf_exchange_results(struct iperf_test *);
int iperf_init_test(struct iperf_test *);
int iperf_create_send_timers(struct iperf_test *);
@@ -301,6 +311,7 @@
int iperf_handle_message_server(struct iperf_test *);
int iperf_create_pidfile(struct iperf_test *);
int iperf_delete_pidfile(struct iperf_test *);
+void iperf_check_total_rate(struct iperf_test *, iperf_size_t);
/* JSON output routines. */
int iperf_json_start(struct iperf_test *);
@@ -348,6 +359,8 @@
IEBADFORMAT = 24, // Bad format argument to -f
IEREVERSEBIDIR = 25, // Iperf cannot be both reverse and bidirectional
IEBADPORT = 26, // Bad port number
+ IETOTALRATE = 27, // Total required bandwidth is larger than server's limit
+ IETOTALINTERVAL = 28, // Invalid time interval for calculating average data rate
/* Test errors */
IENEWTEST = 100, // Unable to create a new test (check perror)
IEINITTEST = 101, // Test initialization failed (check perror)
diff --git a/src/iperf_auth.c b/src/iperf_auth.c
index 46211e0..eb4610f 100644
--- a/src/iperf_auth.c
+++ b/src/iperf_auth.c
@@ -174,6 +174,7 @@
BIO* bio = BIO_new(BIO_s_mem());
BIO_write(bio, key, key_len);
+ free(key);
EVP_PKEY *pkey = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL);
BIO_free(bio);
return (pkey);
@@ -199,6 +200,7 @@
BIO* bio = BIO_new(BIO_s_mem());
BIO_write(bio, key, key_len);
+ free(key);
EVP_PKEY *pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
BIO_free(bio);
return (pkey);
@@ -304,7 +306,7 @@
return (0); //success
}
-int decode_auth_setting(int enable_debug, char *authtoken, EVP_PKEY *private_key, char **username, char **password, time_t *ts){
+int decode_auth_setting(int enable_debug, const char *authtoken, EVP_PKEY *private_key, char **username, char **password, time_t *ts){
unsigned char *encrypted_b64 = NULL;
size_t encrypted_len_b64;
Base64Decode(authtoken, &encrypted_b64, &encrypted_len_b64);
diff --git a/src/iperf_client_api.c b/src/iperf_client_api.c
index 20ea6fd..d0edf7d 100644
--- a/src/iperf_client_api.c
+++ b/src/iperf_client_api.c
@@ -1,5 +1,5 @@
/*
- * iperf, Copyright (c) 2014-2018, The Regents of the University of
+ * iperf, Copyright (c) 2014-2020, The Regents of the University of
* California, through Lawrence Berkeley National Laboratory (subject
* to receipt of any required approvals from the U.S. Dept. of
* Energy). All rights reserved.
@@ -478,7 +478,7 @@
/* Start the client and connect to the server */
if (iperf_connect(test) < 0)
- return -1;
+ goto cleanup_and_fail;
/* Begin calculating CPU utilization */
cpu_util(NULL);
@@ -492,12 +492,12 @@
result = select(test->max_fd + 1, &read_set, &write_set, NULL, timeout);
if (result < 0 && errno != EINTR) {
i_errno = IESELECT;
- return -1;
+ goto cleanup_and_fail;
}
if (result > 0) {
if (FD_ISSET(test->ctrl_sck, &read_set)) {
if (iperf_handle_message_client(test) < 0) {
- return -1;
+ goto cleanup_and_fail;
}
FD_CLR(test->ctrl_sck, &read_set);
}
@@ -521,17 +521,17 @@
if (test->mode == BIDIRECTIONAL)
{
if (iperf_send(test, &write_set) < 0)
- return -1;
+ goto cleanup_and_fail;
if (iperf_recv(test, &read_set) < 0)
- return -1;
+ goto cleanup_and_fail;
} else if (test->mode == SENDER) {
// Regular mode. Client sends.
if (iperf_send(test, &write_set) < 0)
- return -1;
+ goto cleanup_and_fail;
} else {
// Reverse mode. Client receives.
if (iperf_recv(test, &read_set) < 0)
- return -1;
+ goto cleanup_and_fail;
}
@@ -557,7 +557,7 @@
cpu_util(test->cpu_util);
test->stats_callback(test);
if (iperf_set_send_state(test, TEST_END) != 0)
- return -1;
+ goto cleanup_and_fail;
}
}
// If we're in reverse mode, continue draining the data
@@ -567,7 +567,7 @@
// from the client side.
else if (test->mode == RECEIVER && test->state == TEST_END) {
if (iperf_recv(test, &read_set) < 0)
- return -1;
+ goto cleanup_and_fail;
}
}
@@ -582,4 +582,11 @@
iflush(test);
return 0;
+
+ cleanup_and_fail:
+ iperf_client_end(test);
+ if (test->json_output)
+ iperf_json_finish(test);
+ iflush(test);
+ return -1;
}
diff --git a/src/iperf_config.h.in b/src/iperf_config.h.in
index e543cbd..1a7cbec 100644
--- a/src/iperf_config.h.in
+++ b/src/iperf_config.h.in
@@ -40,7 +40,7 @@
#undef HAVE_SCHED_SETAFFINITY
/* Have SCTP support. */
-#undef HAVE_SCTP
+#undef HAVE_SCTP_H
/* Define to 1 if you have the `sendfile' function. */
#undef HAVE_SENDFILE
diff --git a/src/iperf_error.c b/src/iperf_error.c
index c6e5ee4..cfe4cbd 100644
--- a/src/iperf_error.c
+++ b/src/iperf_error.c
@@ -35,12 +35,25 @@
int gerror;
+char iperf_timestrerr[100];
+
/* Do a printf to stderr. */
void
iperf_err(struct iperf_test *test, const char *format, ...)
{
va_list argp;
char str[1000];
+ time_t now;
+ struct tm *ltm = NULL;
+ char *ct = NULL;
+
+ /* Timestamp if requested */
+ if (test != NULL && test->timestamps) {
+ time(&now);
+ ltm = localtime(&now);
+ strftime(iperf_timestrerr, sizeof(iperf_timestrerr), test->timestamp_format, ltm);
+ ct = iperf_timestrerr;
+ }
va_start(argp, format);
vsnprintf(str, sizeof(str), format, argp);
@@ -48,9 +61,15 @@
cJSON_AddStringToObject(test->json_top, "error", str);
else
if (test && test->outfile && test->outfile != stdout) {
+ if (ct) {
+ fprintf(test->outfile, "%s", ct);
+ }
fprintf(test->outfile, "iperf3: %s\n", str);
}
else {
+ if (ct) {
+ fprintf(stderr, "%s", ct);
+ }
fprintf(stderr, "iperf3: %s\n", str);
}
va_end(argp);
@@ -62,6 +81,17 @@
{
va_list argp;
char str[1000];
+ time_t now;
+ struct tm *ltm = NULL;
+ char *ct = NULL;
+
+ /* Timestamp if requested */
+ if (test != NULL && test->timestamps) {
+ time(&now);
+ ltm = localtime(&now);
+ strftime(iperf_timestrerr, sizeof(iperf_timestrerr), "%c ", ltm);
+ ct = iperf_timestrerr;
+ }
va_start(argp, format);
vsnprintf(str, sizeof(str), format, argp);
@@ -70,9 +100,15 @@
iperf_json_finish(test);
} else
if (test && test->outfile && test->outfile != stdout) {
+ if (ct) {
+ fprintf(test->outfile, "%s", ct);
+ }
fprintf(test->outfile, "iperf3: %s\n", str);
}
else {
+ if (ct) {
+ fprintf(stderr, "%s", ct);
+ }
fprintf(stderr, "iperf3: %s\n", str);
}
va_end(argp);
@@ -385,7 +421,13 @@
case IEREVERSEBIDIR:
snprintf(errstr, len, "cannot be both reverse and bidirectional");
break;
-
+ case IETOTALRATE:
+ snprintf(errstr, len, "total required bandwidth is larger than server limit");
+ break;
+ default:
+ snprintf(errstr, len, "int_errno=%d", int_errno);
+ perr = 1;
+ break;
}
/* Append the result of strerror() or gai_strerror() if appropriate */
diff --git a/src/iperf_locale.c b/src/iperf_locale.c
index 2dd5a5b..d5a5354 100644
--- a/src/iperf_locale.c
+++ b/src/iperf_locale.c
@@ -109,6 +109,9 @@
" -J, --json output in JSON format\n"
" --logfile f send output to a log file\n"
" --forceflush force flushing output at every interval\n"
+ " --timestamps <format> emit a timestamp at the start of each output line\n"
+ " (using optional format string as per strftime(3))\n"
+
" -d, --debug emit debugging output\n"
" -v, --version show version information and quit\n"
" -h, --help show this message and quit\n"
@@ -117,6 +120,9 @@
" -D, --daemon run the server as a daemon\n"
" -I, --pidfile file write PID file\n"
" -1, --one-off handle one client connection then exit\n"
+ " --server-bitrate-limit #[KMG][/#] server's total bit rate limit (default 0 = no limit)\n"
+ " (optional slash and number of secs interval for averaging\n"
+ " total data rate. Default is 5 seconds)\n"
#if defined(HAVE_SSL)
" --rsa-private-key-path path to the RSA private key used to decrypt\n"
" authentication credentials\n"
@@ -125,11 +131,11 @@
#endif //HAVE_SSL
"Client specific:\n"
" -c, --client <host> run in client mode, connecting to <host>\n"
-#if defined(HAVE_SCTP)
+#if defined(HAVE_SCTP_H)
" --sctp use SCTP rather than TCP\n"
" -X, --xbind <name> bind SCTP association to links\n"
" --nstreams # number of SCTP streams\n"
-#endif /* HAVE_SCTP */
+#endif /* HAVE_SCTP_H */
" -u, --udp use UDP rather than TCP\n"
" --connect-timeout # timeout for control connection setup (ms)\n"
" -b, --bitrate #[KMG][/#] target bitrate in bits/sec (0 for unlimited)\n"
diff --git a/src/iperf_sctp.c b/src/iperf_sctp.c
index 0bc98ba..e0c1ec1 100644
--- a/src/iperf_sctp.c
+++ b/src/iperf_sctp.c
@@ -57,7 +57,7 @@
int
iperf_sctp_recv(struct iperf_stream *sp)
{
-#if defined(HAVE_SCTP)
+#if defined(HAVE_SCTP_H)
int r;
r = Nread(sp->socket, sp->buffer, sp->settings->blksize, Psctp);
@@ -78,7 +78,7 @@
#else
i_errno = IENOSCTP;
return -1;
-#endif /* HAVE_SCTP */
+#endif /* HAVE_SCTP_H */
}
@@ -89,7 +89,7 @@
int
iperf_sctp_send(struct iperf_stream *sp)
{
-#if defined(HAVE_SCTP)
+#if defined(HAVE_SCTP_H)
int r;
r = Nwrite(sp->socket, sp->buffer, sp->settings->blksize, Psctp);
@@ -103,7 +103,7 @@
#else
i_errno = IENOSCTP;
return -1;
-#endif /* HAVE_SCTP */
+#endif /* HAVE_SCTP_H */
}
@@ -115,7 +115,7 @@
int
iperf_sctp_accept(struct iperf_test * test)
{
-#if defined(HAVE_SCTP)
+#if defined(HAVE_SCTP_H)
int s;
signed char rbuf = ACCESS_DENIED;
char cookie[COOKIE_SIZE];
@@ -148,7 +148,7 @@
#else
i_errno = IENOSCTP;
return -1;
-#endif /* HAVE_SCTP */
+#endif /* HAVE_SCTP_H */
}
@@ -159,7 +159,7 @@
int
iperf_sctp_listen(struct iperf_test *test)
{
-#if defined(HAVE_SCTP)
+#if defined(HAVE_SCTP_H)
struct addrinfo hints, *res;
char portstr[6];
int s, opt, saved_errno;
@@ -270,7 +270,7 @@
#else
i_errno = IENOSCTP;
return -1;
-#endif /* HAVE_SCTP */
+#endif /* HAVE_SCTP_H */
}
@@ -281,7 +281,7 @@
int
iperf_sctp_connect(struct iperf_test *test)
{
-#if defined(HAVE_SCTP)
+#if defined(HAVE_SCTP_H)
int s, opt, saved_errno;
char portstr[6];
struct addrinfo hints, *local_res, *server_res;
@@ -527,7 +527,7 @@
#else
i_errno = IENOSCTP;
return -1;
-#endif /* HAVE_SCTP */
+#endif /* HAVE_SCTP_H */
}
@@ -535,12 +535,12 @@
int
iperf_sctp_init(struct iperf_test *test)
{
-#if defined(HAVE_SCTP)
+#if defined(HAVE_SCTP_H)
return 0;
#else
i_errno = IENOSCTP;
return -1;
-#endif /* HAVE_SCTP */
+#endif /* HAVE_SCTP_H */
}
@@ -552,7 +552,7 @@
int
iperf_sctp_bindx(struct iperf_test *test, int s, int is_server)
{
-#if defined(HAVE_SCTP)
+#if defined(HAVE_SCTP_H)
struct addrinfo hints;
char portstr[6];
char *servname;
@@ -701,5 +701,5 @@
#else
i_errno = IENOSCTP;
return -1;
-#endif /* HAVE_SCTP */
+#endif /* HAVE_SCTP_H */
}
diff --git a/src/iperf_server_api.c b/src/iperf_server_api.c
index 40d99bc..e2ddf7f 100644
--- a/src/iperf_server_api.c
+++ b/src/iperf_server_api.c
@@ -1,5 +1,5 @@
/*
- * iperf, Copyright (c) 2014-2018 The Regents of the University of
+ * iperf, Copyright (c) 2014-2020 The Regents of the University of
* California, through Lawrence Berkeley National Laboratory (subject
* to receipt of any required approvals from the U.S. Dept. of
* Energy). All rights reserved.
@@ -354,6 +354,15 @@
static void
cleanup_server(struct iperf_test *test)
{
+ struct iperf_stream *sp;
+
+ /* Close open streams */
+ SLIST_FOREACH(sp, &test->streams, streams) {
+ FD_CLR(sp->socket, &test->read_set);
+ FD_CLR(sp->socket, &test->write_set);
+ close(sp->socket);
+ }
+
/* Close open test sockets */
if (test->ctrl_sck) {
close(test->ctrl_sck);
@@ -437,12 +446,20 @@
while (test->state != IPERF_DONE) {
+ // Check if average transfer rate was exceeded (condition set in the callback routines)
+ if (test->bitrate_limit_exceeded) {
+ cleanup_server(test);
+ i_errno = IETOTALRATE;
+ return -1;
+ }
+
memcpy(&read_set, &test->read_set, sizeof(fd_set));
memcpy(&write_set, &test->write_set, sizeof(fd_set));
iperf_time_now(&now);
timeout = tmr_timeout(&now);
result = select(test->max_fd + 1, &read_set, &write_set, NULL, timeout);
+
if (result < 0 && errno != EINTR) {
cleanup_server(test);
i_errno = IESELECT;
@@ -596,6 +613,17 @@
}
}
test->prot_listener = -1;
+
+ /* Ensure that total requested data rate is not above limit */
+ iperf_size_t total_requested_rate = test->num_streams * test->settings->rate * (test->mode == BIDIRECTIONAL? 2 : 1);
+ if (test->settings->bitrate_limit > 0 && total_requested_rate > test->settings->bitrate_limit) {
+ iperf_err(test, "Client total requested throughput rate of %" PRIu64 " bps exceeded %" PRIu64 " bps limit",
+ total_requested_rate, test->settings->bitrate_limit);
+ cleanup_server(test);
+ i_errno = IETOTALRATE;
+ return -1;
+ }
+
if (iperf_set_send_state(test, TEST_START) != 0) {
cleanup_server(test);
return -1;
@@ -647,7 +675,7 @@
return -1;
}
}
- }
+ }
}
if (result == 0 ||
diff --git a/src/iperf_udp.c b/src/iperf_udp.c
index ab6be5e..2fd7bf5 100644
--- a/src/iperf_udp.c
+++ b/src/iperf_udp.c
@@ -52,7 +52,13 @@
#if defined(HAVE_INTTYPES_H)
# include <inttypes.h>
#else
-# define PRIu64 "llu"
+# ifndef PRIu64
+# if sizeof(long) == 8
+# define PRIu64 "lu"
+# else
+# define PRIu64 "llu"
+# endif
+# endif
#endif
/* iperf_udp_recv
diff --git a/src/iperf_util.c b/src/iperf_util.c
index 412397a..9ca1eec 100644
--- a/src/iperf_util.c
+++ b/src/iperf_util.c
@@ -111,7 +111,7 @@
*/
void
-make_cookie(char *cookie)
+make_cookie(const char *cookie)
{
unsigned char *out = (unsigned char*)cookie;
size_t pos;
@@ -267,7 +267,7 @@
numfeatures++;
#endif /* HAVE_FLOWLABEL */
-#if defined(HAVE_SCTP)
+#if defined(HAVE_SCTP_H)
if (numfeatures > 0) {
strncat(features, ", ",
sizeof(features) - strlen(features) - 1);
@@ -275,7 +275,7 @@
strncat(features, "SCTP",
sizeof(features) - strlen(features) - 1);
numfeatures++;
-#endif /* HAVE_SCTP */
+#endif /* HAVE_SCTP_H */
#if defined(HAVE_TCP_CONGESTION)
if (numfeatures > 0) {
@@ -402,7 +402,7 @@
/* Debugging routine to dump out an fd_set. */
void
-iperf_dump_fdset(FILE *fp, char *str, int nfds, fd_set *fds)
+iperf_dump_fdset(FILE *fp, const char *str, int nfds, fd_set *fds)
{
int fd;
int comma;
diff --git a/src/iperf_util.h b/src/iperf_util.h
index 76bfd20..b109af2 100644
--- a/src/iperf_util.h
+++ b/src/iperf_util.h
@@ -54,7 +54,7 @@
cJSON* iperf_json_printf(const char *format, ...);
-void iperf_dump_fdset(FILE *fp, char *str, int nfds, fd_set *fds);
+void iperf_dump_fdset(FILE *fp, const char *str, int nfds, fd_set *fds);
#ifndef HAVE_DAEMON
extern int daemon(int nochdir, int noclose);
diff --git a/src/net.c b/src/net.c
index b475ed0..8fde9c3 100644
--- a/src/net.c
+++ b/src/net.c
@@ -121,7 +121,7 @@
/* make connection to server */
int
-netdial(int domain, int proto, char *local, int local_port, char *server, int port, int timeout)
+netdial(int domain, int proto, const char *local, int local_port, const char *server, int port, int timeout)
{
struct addrinfo hints, *local_res, *server_res;
int s, saved_errno;
@@ -218,7 +218,7 @@
/***************************************************************/
int
-netannounce(int domain, int proto, char *local, int port)
+netannounce(int domain, int proto, const char *local, int port)
{
struct addrinfo hints, *res;
char portstr[6];
diff --git a/src/net.h b/src/net.h
index 3738d6a..80a2161 100644
--- a/src/net.h
+++ b/src/net.h
@@ -28,8 +28,8 @@
#define __NET_H
int timeout_connect(int s, const struct sockaddr *name, socklen_t namelen, int timeout);
-int netdial(int domain, int proto, char *local, int local_port, char *server, int port, int timeout);
-int netannounce(int domain, int proto, char *local, int port);
+int netdial(int domain, int proto, const char *local, int local_port, const char *server, int port, int timeout);
+int netannounce(int domain, int proto, const char *local, int port);
int Nread(int fd, char *buf, size_t count, int prot);
int Nwrite(int fd, const char *buf, size_t count, int prot) /* __attribute__((hot)) */;
int has_sendfile(void);
diff --git a/test_commands.sh b/test_commands.sh
index 2ed0f82..146d1da 100755
--- a/test_commands.sh
+++ b/test_commands.sh
@@ -29,12 +29,20 @@
# force V6
./src/iperf3 -c $host -6 -t 5
./src/iperf3 -c $host -6 -u -t 5
+# FQ rate
+./src/iperf3 -c $host -V -t 5 --fq-rate 5m
+./src/iperf3 -c $host -u -V -t 5 --fq-rate 5m
+# SCTP
+./src/iperf3 -c $host --sctp -V -t 5
# parallel streams
./src/iperf3 -c $host -P 3 -t 5
./src/iperf3 -c $host -u -P 3 -t 5
# reverse mode
./src/iperf3 -c $host -P 2 -t 5 -R
./src/iperf3 -c $host -u -P 2 -t 5 -R
+# bidirectional mode
+./src/iperf3 -c $host -P 2 -t 5 --bidir
+./src/iperf3 -c $host -u -P 2 -t 5 --bidir
# zero copy
./src/iperf3 -c $host -Z -t 5
./src/iperf3 -c $host -Z -t 5 -R