diff --git a/docs/_esnet/templates/navbar.html b/docs/_esnet/templates/navbar.html
index 2b100398b..c763d8c8f 100644
--- a/docs/_esnet/templates/navbar.html
+++ b/docs/_esnet/templates/navbar.html
@@ -9,8 +9,6 @@
{%- block sidebarlogo %}
-
-
{%- if logo %}
{%- endif %}
{%- endblock %}
{% if theme_navbar_title -%}{{ theme_navbar_title|e }}{%- else -%}{{ project|e }}{%- endif -%}
@@ -36,7 +34,7 @@
{{ link[0] }}
{%- endfor %}
{% endif %}
-
+
{% block navbarextra %}
{% endblock %}
{% if theme_source_link_position == "nav" %}
diff --git a/docs/conf.py b/docs/conf.py
index 5ae7201f6..82bcc22a4 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -45,17 +45,17 @@
# General information about the project.
project = u'iperf3'
-copyright = u'2014-2023, ESnet'
+copyright = u'2014-2026, ESnet'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
-version = '3.13'
+version = '3.21'
# The full version, including alpha/beta/rc tags.
-release = '3.13'
+release = '3.21'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
@@ -112,7 +112,7 @@
"navbar_site_name": "Section",
"navbar_links": [
("Index", "genindex"),
- ("ESnet", "https://www.es.net", True),
+ ("
", "https://www.es.net", True),
],
}
@@ -128,7 +128,8 @@
# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
-html_logo = "_esnet/static/ESnet_Final_Logos_All_Blue_Circle_Stamp_RGB.png"
+html_logo = "_static/esnet/ESnet_Final_Logos_All_Blue_Circle_Stamp_RGB.png"
+
# The name of an image file (within the static path) to use as favicon of the
@@ -158,7 +159,8 @@
#html_use_smartypants = True
# Custom sidebar templates, maps document names to template names.
-html_sidebars = {'index': None, 'search': None, '*': ['localtoc.html']}
+#html_sidebars = {'index': None, 'search': None, '*': ['localtoc.html']}
+html_sidebars = {'index': [], 'search': [], '**': ['localtoc.html']}
# Additional templates that should be rendered to pages, maps page names to
# template names.
@@ -174,7 +176,7 @@
#html_split_index = False
# If true, links to the reST sources are added to the pages.
-#html_show_sourcelink = True
+html_show_sourcelink = False
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
#html_show_sphinx = True
diff --git a/docs/dev.rst b/docs/dev.rst
index 98548b478..a8de99b5f 100644
--- a/docs/dev.rst
+++ b/docs/dev.rst
@@ -3,10 +3,10 @@ iperf3 Development
The iperf3 project is hosted on GitHub at:
-http://github.com/esnet/iperf
+https://github.com/esnet/iperf
This site includes the source code repository, issue tracker, and
-wiki.
+discussion forums.
Mailing Lists
-------------
@@ -14,12 +14,38 @@ Mailing Lists
The developer list for iperf3 is: iperf-dev@googlegroups.com.
Information on joining the mailing list can be found at:
-http://groups.google.com/group/iperf-dev
+https://groups.google.com/group/iperf-dev
-There is, at the moment, no mailing list for user questions, although
-a low volume of inquiries on the developer list is probably
-acceptable. If necessary, a user-oriented mailing list might be
-created in the future.
+Project Constituencies
+----------------------
+
+iperf3 has several different audiences. The development priorities
+for iperf3 are based around the needs of those various types of users,
+in roughly descending priority order.
+
+1. perfSONAR: iperf3 was developed initially to serve as the bandwidth
+ tester for the perfSONAR measurement suite
+ (https://www.perfsonar.net). This is considered the primary user
+ base for iperf3 and requests from the developers of this project
+ will generally get the highest priority.
+
+2. ESnet and R&E Networking: iperf3 is often used as a standalone tool
+ for testing within ESnet (https://www.es.net/) and other R&E
+ (Research and Education) networking environments. These settings
+ are similar to those encountered in many networks using
+ perfSONAR. They often include wide-area networks (sometimes with
+ national or international scale) that have bitrates up to (and
+ sometimes exceeding) 100Gbps speeds.
+
+3. The Internet community: iperf3 has been found to be useful by the
+ Internet community at large. Users in this community may have a
+ wide range of networking needs and environments, which might
+ include different speeds and types of networks (such as homelabs,
+ residential broadband, corporate networks), operating systems
+ (non-UNIX or embedded OSs). Catering to the desires of this
+ community can be challenging, but might be undertaken if this can
+ be done without compromising the needs of the first two
+ communities.
Bug Reports
-----------
@@ -30,44 +56,51 @@ Then submit to the iperf3 issue tracker on GitHub:
https://github.com/esnet/iperf/issues
+For reporting potential security issues, please contact the developers at
+iperf@es.net.
+
**Note:** Issues submitted to the old iperf3 issue tracker on Google
Code (or comments to existing issues on the Google Code issue tracker)
will be ignored.
-Changes from iperf 2.x
-----------------------
-
-New options (not necessarily complete, please refer to the manual page
-for a complete list of iperf3 options)::
-
- -V, --verbose more detailed output than before
- -J, --json output in JSON format
- -Z, --zerocopy use a 'zero copy' sendfile() method of sending data
- -O, --omit N omit the first n seconds (to ignore slowstart)
- -T, --title str prefix every output line with this string
- -F, --file name xmit/recv the specified file
- -A, --affinity n/n,m set CPU affinity (Linux and FreeBSD only)
- -k, --blockcount #[KMG] number of blocks (packets) to transmit (instead
- of -t or -n)
- -L, --flowlabel set IPv6 flow label (Linux only)
-
-Changed flags::
-
- -C, --linux-congestion set congestion control algorithm (Linux only)
- (-Z in iperf2)
-
-
-Deprecated flags (currently no plans to support)::
-
- -d, --dualtest Do a bidirectional test simultaneously
- -r, --tradeoff Do a bidirectional test individually
- -T, --ttl time-to-live, for multicast (default 1)
- -x, --reportexclude [CDMSV] exclude C(connection) D(data) M(multicast)
- S(settings) V(server) reports
- -y, --reportstyle C report as a Comma-Separated Values
-
-Also deprecated is the ability to set the options via environment
-variables.
+Code Submissions
+----------------
+
+Submissions of potential code changes can be made via
+GitHub's Pull Request mechanism:
+
+https://github.com/esnet/iperf/pulls
+
+The iperf3 development team is grateful for all contributions,
+particularly those that fix bugs or security issues.
+
+Please note that iperf3 is an extremely complicated program, primarily
+due to the large number of options it supports. (One of the primary
+developers has said repeatedly for years that there are "way too many
+options".) These options often interact in various ways that might not
+be initially obvious to those unfamiliar with the iperf3 code base (or
+even those who have been reading it for years), so adding new features
+has in the past caused bugs due to unforeseen interactions wtih other
+bugs. The sheer number of combinations of options makes testing
+difficult for both humans and automated testing systems.
+
+In addition the developers need to be able to maintain any code
+submissions. This can be difficult when dealing with new features
+outside the maintainers' experience.
+
+Finally every new feature comes at a cost (principally in terms of
+evaluating new code and later maintenance). The maintainers need to
+prioritize their limited time and effort to be able to support its
+main users (as detailed above, those are the perfSONAR community,
+ESnet, and R&E networking). This means that unrelated requests from
+the community might not get consideration.
+
+As of this writing (late 2025), the developers are unlikely to add any
+major new features into the iperf3 codebase unless they're known to be
+generally useful to the main audiences of this software. To those
+users who nevertheless want to add something to iperf3, please discuss
+your ideas with the iperf3 maintainers, preferably before starting
+work.
Known Issues
------------
@@ -79,9 +112,6 @@ tracker. These issues are either open (indicating no solution
currently exists) or closed with the notation that no further attempts
to solve the problem are currently being made:
-* The ``-Z`` flag sometimes causes the iperf3 client to hang on OSX.
- (Issue #129)
-
* When specifying the TCP buffer size using the ``-w`` flag on Linux,
the Linux kernel automatically doubles the value passed in to
compensate for overheads. (This can be observed by using
@@ -97,17 +127,17 @@ to solve the problem are currently being made:
* On some platforms (observed on at least one version of Ubuntu
Linux), it might be necessary to invoke ``ldconfig`` manually after
doing a ``make install`` before the ``iperf3`` executable can find
- its shared library. (Issue #153)
+ its shared library.
* The results printed on the server side at the end of a test do not
correctly reflect the client-side measurements. This is due to the
ordering of computing and transferring results between the client
- and server. (Issue #293)
+ and server.
* The server could have a very short measurement reporting interval at
the end of a test (particularly a UDP test), containing few or no
packets. This issue is due to an artifact of timing between the
- client and server. (Issue #278)
+ client and server.
There are, of course, many other open and closed issues in the issue
tracker.
@@ -116,7 +146,7 @@ Versioning
----------
iperf3 version numbers use (roughly) a `Semantic Versioning
-`_ scheme, in which version numbers consist of
+`_ scheme, in which version numbers consist of
three parts: *MAJOR.MINOR.PATCH*
The developers increment the:
@@ -127,72 +157,126 @@ The developers increment the:
* *PATCH* version when making backwards-compatible bug fixes.
+The iperf3 developers aim to produce two software releases per year,
+in April and October. These releases, indicated by a new minor version
+number, are essentially snapshots of the `master` codeline, and might
+contain new features or functionality.
+
+Between these releases, the developers might need to release patch
+releases to address major bugs or security vulnerabilities. These will
+generally be based on the most recent minor release and will increment
+the patch version number component. Typically they will only contain
+selected fixes cherry-picked from the `master` codeline, which will be
+rolled up into the next minor release.
+
Release Engineering Checklist
-----------------------------
-1. Update the ``README`` and ``RELNOTES.md`` files to be accurate. Make sure
- that the "Known Issues" section of the ``README`` file and in this document
- are up to date.
+1. Start from a clean source tree (be sure that ``git status`` emits
+ no output). Also ensure up-to-date installs of ``autoconf`` and
+ ``automake``.
+
+2. From the tip of ``master`` (or other appropriate integration
+ branch), create a branch for release engineering changes. This
+ should probably be named something along the lines of
+ ``releng-3.20``. So::
+
+ git branch releng-3.20 # create short-lived branch
+ git checkout releng-3.20 # check it out
+
+3. Ensure that ``README.md`` and ``LICENSE`` have correct copyright
+ dates.::
+
+ vi README.md
+ vi LICENSE
+
+4. Update the ``README.md`` and ``RELNOTES.md`` files to be
+ accurate.::
+
+ vi RELNOTES.md # update version number and release date
-2. Compose a release announcement. Most of the release announcement
+5. Compose a release announcement. Most of the release announcement
can be written before tagging. Usually the previous version's
announcement can be used as a starting point.
-3. Preferably starting from a clean source tree (be sure that ``git
- status`` emits no output), make the changes necessary to produce
+6. Make the changes necessary to produce
the new version, such as bumping version numbers::
- vi RELNOTES.md # update version number and release date
vi configure.ac # update version parameter in AC_INIT
- vi src/iperf3.1 # update manpage revision date if needed
- vi src/libiperf.3 # update manpage revision date if needed
+ # (there should not be any "+" in artifacts)
+ vi src/iperf3.1 # update manpage revision date (only if needed)
+ vi src/libiperf.3 # update manpage revision date (only if needed)
git commit -a # commit changes to the local repository only
+ # (commit log should mention version number)
./bootstrap.sh # regenerate configure script, etc.
+ # do this on a platform with a recent
+ # autotools/libtools, such as HomeBrew or
+ # FreeBSD ports.
git commit -a # commit changes to the local repository only
+ # (commit can be simply "Regen.")
+ git push --set-upstream origin releng-3.20 # Push branch for review
- # Assuming that $VERSION is the version number to be released...
- ./make_release tag $VERSION # this creates a tag in the local repo
- ./make_release tar $VERSION # create tarball and compute SHA256 hash
+7. Create a pull request for this branch (e.g. ``releng-3.20``)
- These steps should be done on a platform with a relatively recent
- version of autotools / libtools. Examples are MacOS / MacPorts or
- FreeBSD. The versions of these tools in CentOS 6 are somewhat
- older and probably should be avoided.
+8. Review and get approval for the pull request
- The result will be a release artifact that should be used for
- pre-testing.
+9. Merge pull request to `master` or other appropriate integration
+ branch.
-4. Stage the tarball (and a file containing the SHA256 hash) to the
- download site. Currently this is located on ``downloads.es.net``.
+10. Create tag and tarfile.
+ The result will be release artifacts that should be used for
+ pre-testing. One will be a compressed tarball
+ (e.g. ``iperf-3.20.tar.gz``) and the other will contain SHA256
+ checksum (e.g. ``iperf-3.20.tar.gz.sha256``)::
+
+ ./make_release tag 3.20 # this creates a tag in the local repo
+ ./make_release tar 3.20 # create tarball and compute SHA256 hash
-5. From another host, test the link in the release announcement by
+6. Stage the tarball (and a file containing the SHA256 hash) to the
+ download site. Currently this is located on ``downloads.es.net``
+ (accessed via ``downloads-mgt.es.net`` from the ESnet internal network)
+ in the directory ``/var/www/html/pub/iperf/``.
+
+7. From another host, test the link in the release announcement by
downloading a fresh copy of the file and verifying the SHA256
checksum. Checking all other links in the release announcement is
strongly recommended as well.
-6. Also verify (with file(1)) that the tarball is actually a gzipped
+ The link to the tarball will be something of the form
+ ``https://downloads.es.net/pub/iperf/iperf-3.20.tar.gz``. If
+ composing a release announcement using a HTML-aware editor, verify
+ the link targets point to the correct artifacts.
+
+8. Also verify (with file(1)) that the tarball is actually a gzipped
tarball.
-7. For extra points, actually try downloading, compiling, and
- smoke-testing the results of the tarball on all supported
- platforms.
+9. Try downloading, compiling, and smoke-testing the results of the
+ tarball on all supported platforms.
-8. Plug the SHA256 checksum into the release announcement.
+10. Verify that the version string in ``iperf3 --version`` matches the
+ version number of the artifacts.
-9. PGP-sign the release announcement text using ``gpg --clearsign``.
- The signed announcement will be sent out in a subsequent emails,
- but could also be archived. Decoupling the signing from emailing
- allows a signed release announcement to be resent via email or sent
- by other, non-email means.
+11. (optional) PGP-sign the release announcement text using ``gpg
+ --clearsign``. The signed announcement will be sent out in a
+ subsequent emails, but could also be archived. Decoupling the
+ signing from emailing allows a signed release announcement to be
+ resent via email or sent by other, non-email means.
-10. At this point, the release can and should be considered
+12. At this point, the release can and should be considered
finalized. To commit the release-engineering-related changes to
GitHub and make them public, push them out thusly::
- git push # Push version changes
git push --tags # Push the new tag to the GitHub repo
-11. Send the PGP-signed release announcement to the following
+13. Update GitHub Releases with the current release notes. Start from:
+ ``https://github.com/esnet/iperf/releases/new``. Remember to
+ properly select the tag from the dropdown menu. Check "Set as the
+ latest release" and (optionally) "Create a discussion for this
+ release".
+
+14. Attach the tarball and the SHA256 file to the release.
+
+15. Send the release announcement to the following
addresses. Remember to turn off signing in the MUA, if
applicable. Remember to check the source address when posting to
lists, as "closed" list will reject posting from all from
@@ -214,19 +298,39 @@ Release Engineering Checklist
sending process by sending a copy to oneself first and attempting
to verify the signature is highly encouraged.
-12. Update the iperf3 Project News section of the documentation site
+16. Announce the new release in the #iperf3 channel in ESnet Slack.
+
+17. Create and submit a Monday Ping entry for the new release.
+
+18. Update the iperf3 Project News section of the documentation site
to announce the new release (see ``docs/news.rst`` and
- ``docs/conf.py`` in the source tree) and deploy a new build of the
- documentation to GitHub Pages.
+ ``docs/conf.py`` in the source tree). Be sure to double-check version
+ numbers and copyright dates.
-13. If an update to the on-line manual page is needed, it can be
- generated with this sequence of commands (tested on CentOS 7) and
+19. If an update to the on-line manual page is needed, it can be
+ generated with this sequence of commands and
import the result into ``invoking.rst``::
TERM=
export TERM
nroff -Tascii -c -man src/iperf3.1 | ul | sed 's/^/ /' > iperf3.txt
+20. Commit documentation changes after viewing the rendered HTML, and
+ deploy a new build of the documentation to GitHub Pages.
+
+21. Update the version number in ``configure.ac`` to some
+ post-release number (with a "+") and regenerate::
+
+ vi configure.ac # update version in AC_INIT, add "+"
+ git commit configure.ac # commit changes to local repository
+ # commit log should mention
+ # "post-release version bump"
+ ./bootstrap.sh # regenerate configure script, etc.
+ git commit -a # commit changes to local repository
+ # (commit can be simply "Regen.")
+ # test
+ git push
+
Code Authors
------------
diff --git a/docs/faq.rst b/docs/faq.rst
index 7b700269b..c10f70893 100644
--- a/docs/faq.rst
+++ b/docs/faq.rst
@@ -12,7 +12,7 @@ What is the history of iperf3, and what is the difference between iperf2 and ipe
base. For this reason, it was decided to make the tool single
threaded, and not worry about backwards compatibility with
iperf2. Many of the feature requests for iperf3 came from the
- perfSONAR project (http://www.perfsonar.net).
+ perfSONAR project (https://www.perfsonar.net).
Then in 2014, Bob (Robert) McMahon from Broadcom restarted
development of iperf2 (See
@@ -22,7 +22,14 @@ What is the history of iperf3, and what is the difference between iperf2 and ipe
current development is focused is on using UDP for latency testing, as well
as broad platform support.
- As of this writing (2017), both iperf2 and iperf3 are being actively
+ In 2023, iperf3 was modified and restructured to support
+ multi-threading, so that it uses one thread per test stream. This
+ allows it to use multiple CPU cores during tests, which in turn
+ permit it to keep up with continually increasing network link and
+ path bandwidths across the backbones of ESnet and other network
+ providers.
+
+ As of this writing (2024), both iperf2 and iperf3 are being actively
(although independently) developed. We recommend being familiar with
both tools, and use whichever tool’s features best match your needs.
@@ -30,9 +37,22 @@ What is the history of iperf3, and what is the difference between iperf2 and ipe
https://fasterdata.es.net/performance-testing/network-troubleshooting-tools/throughput-tool-comparision/
iperf3 parallel stream performance is much less than iperf2. Why?
- iperf3 is single threaded, and iperf2 is multi-threaded. We
- recommend using iperf2 for parallel streams.
- If you want to use multiple iperf3 streams use the method described `here `_.
+ Versions of iperf3 before version 3.16 were all single threaded, and
+ iperf2 is multi-threaded. This could result in a performance gap
+ because iperf3 was only able to use one CPU core on a host, which
+ turned into a bottleneck when trying to do high bitrate tests
+ (faster than about 25 Gbps).
+
+ Beginning with version 3.16, iperf3 is multi-threaded, which allows
+ it to take advantage of multiple CPU cores during a test (one thread
+ per stream). iperf3 has been observed to send and receive
+ approximately 160Gbps on a 200Gbps path in a test involving multiple
+ TCP flows, with little or no tuning.
+
+ Prior to multi-threading support in iperf3, one might need to use
+ the method described `here
+ `_
+ to achieve faster speeds.
I’m trying to use iperf3 on Windows, but having trouble. What should I do?
iperf3 is not officially supported on Windows, but iperf2 is. We
@@ -242,7 +262,7 @@ A file sent using the ``-F`` option got corrupted...what happened?
I have a question regarding iperf3...what's the best way to get help?
Searching on the Internet is a good first step.
- http://stackoverflow.com/ has a number of iperf3-related questions
+ https://stackoverflow.com/ has a number of iperf3-related questions
and answers, but a simple query into your favorite search engine can
also yield some results.
diff --git a/docs/index.rst b/docs/index.rst
index 0f4b4c6d5..486eccbcc 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -3,6 +3,8 @@
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
+.. image:: _static/iperf3-logos-0410_6.png
+
iperf3
======
@@ -22,13 +24,13 @@ original iperf. These include, for example, a zero-copy mode and
optional JSON output. Note that iperf3 is *not* backwards compatible
with the original iperf.
-Primary development for iperf3 takes place on CentOS Linux, FreeBSD,
+Primary development for iperf3 takes place on Ubuntu Linux, FreeBSD,
and macOS. At this time, these are the only officially
supported platforms, however there have been some reports of success
with OpenBSD, Android, and other Linux distributions.
-iperf3 is principally developed by `ESnet `_ /
-`Lawrence Berkeley National Laboratory `_. It
+iperf3 is principally developed by `ESnet `_ /
+`Lawrence Berkeley National Laboratory `_. It
is released under a three-clause BSD license.
iperf2 is no longer being developed by its original maintainers.
diff --git a/docs/invoking.rst b/docs/invoking.rst
index 5b09463fa..c7da7fcbc 100644
--- a/docs/invoking.rst
+++ b/docs/invoking.rst
@@ -29,8 +29,6 @@ the executable.
IPERF3(1) User Manuals IPERF3(1)
-
-
NAME
iperf3 - perform network throughput tests
@@ -52,7 +50,7 @@ the executable.
iperf3 --server
- Note that many iperf3 parameters have both short (-s) and long
+ Note that many iperf3 parameters have both short (-s) and long
(--server) forms. In this section we will generally use the short form
of command-line flags, unless only the long form of a flag is avail-
able.
@@ -64,9 +62,9 @@ the executable.
iperf3 -s -p 5002
After the server is started, it will listen for connections from iperf3
- clients (in other words, the iperf3 program run in client mode). The
+ clients (in other words, the iperf3 program run in client mode). The
client mode can be started using the -c command-line option, which also
- requires a host to which iperf3 should connect. The host can by speci-
+ requires a host to which iperf3 should connect. The host can be speci-
fied by hostname, IPv4 literal, or IPv6 literal:
iperf3 -c iperf3.example.com
@@ -75,7 +73,7 @@ the executable.
iperf3 -c 2001:db8::1
- If the iperf3 server is running on a non-default TCP port, that port
+ If the iperf3 server is running on a non-default TCP port, that port
number needs to be specified on the client as well:
iperf3 -c iperf3.example.com -p 5002
@@ -98,26 +96,27 @@ the executable.
least one line of output per measurement interval (by default a mea-
surement interval lasts for one second, but this can be changed by the
-i option). Each line of output includes (at least) the time since the
- start of the test, amount of data transferred during the interval, and
- the average bitrate over that interval. Note that the values for each
- measurement interval are taken from the point of view of the endpoint
- process emitting that output (in other words, the output on the client
+ start of the test, amount of data transferred during the interval, and
+ the average bitrate over that interval. Note that the values for each
+ measurement interval are taken from the point of view of the endpoint
+ process emitting that output (in other words, the output on the client
shows the measurement interval data for the client.
- At the end of the test is a set of statistics that shows (at least as
- much as possible) a summary of the test as seen by both the sender and
- the receiver, with lines tagged accordingly. Recall that by default
- the client is the sender and the server is the receiver, although as
+ At the end of the test is a set of statistics that shows (at least as
+ much as possible) a summary of the test as seen by both the sender and
+ the receiver, with lines tagged accordingly. Recall that by default
+ the client is the sender and the server is the receiver, although as
indicated above, use of the -R flag will reverse these roles.
- The client can be made to retrieve the server-side output for a given
+ The client can be made to retrieve the server-side output for a given
test by specifying the --get-server-output flag.
Either the client or the server can produce its output in a JSON struc-
ture, useful for integration with other programs, by passing it the -J
- flag. Because the contents of the JSON structure are only completely
+ flag. Normally the contents of the JSON structure are only completely
known after the test has finished, no JSON output will be emitted until
- the end of the test.
+ the end of the test. By enabling line-delimited JSON multiple objects
+ will be emitted to provide a real-time parsable JSON output.
iperf3 has a (overly) large set of command-line options that can be
used to set the parameters of a test. They are given in the "GENERAL
@@ -127,355 +126,403 @@ the executable.
GENERAL OPTIONS
-p, --port n
- set server port to listen on/connect to to n (default 5201)
+ Set server port to listen on/connect to to n (default 5201)
- -f, --format
- [kmgtKMGT] format to report: Kbits/Mbits/Gbits/Tbits
+ -f, --format [kmgtKMGT]
+ Set format to report: Kbits/Mbits/Gbits/Tbits
-i, --interval n
- pause n seconds between periodic throughput reports; default is
- 1, use 0 to disable
+ Pause n seconds between periodic throughput reports; default is
+ 1, use 0 to disable.
-I, --pidfile file
- write a file with the process ID, most useful when running as a
- daemon.
+ Write a file with the process ID. This option is most useful
+ when running as a daemon.
-F, --file name
- Use a file as the source (on the sender) or sink (on the
- receiver) of data, rather than just generating random data or
- throwing it away. This feature is used for finding whether or
- not the storage subsystem is the bottleneck for file transfers.
- It does not turn iperf3 into a file transfer tool. The length,
- attributes, and in some cases contents of the received file may
- not match those of the original file.
+ Use a file as the source (on the sender) or sink (on the re-
+ ceiver) of data, rather than just generating random data or
+ throwing it away. This feature is used for finding whether or
+ not the storage subsystem is the bottleneck for file transfers.
+ It does not turn iperf3 into a file transfer tool. The length,
+ attributes, and in some cases contents of the received file may
+ not match those of the original file. This option is unavailable
+ when doing --udp tests.
-A, --affinity n/n,m
- Set the CPU affinity, if possible (Linux, FreeBSD, and Windows
- only). On both the client and server you can set the local
- affinity by using the n form of this argument (where n is a CPU
- number). In addition, on the client side you can override the
- server's affinity for just that one test, using the n,m form of
- argument. Note that when using this feature, a process will
- only be bound to a single CPU (as opposed to a set containing
+ Set the CPU affinity, if possible (Linux, FreeBSD, and Windows
+ only). On both the client and server you can set the local
+ affinity by using the n form of this argument (where n is a CPU
+ number). In addition, on the client side you can override the
+ server's affinity for just that one test, using the n,m form of
+ argument. Note that when using this feature, a process will
+ only be bound to a single CPU (as opposed to a set containing
potentially multiple CPUs).
-B, --bind host[%dev]
- bind to the specific interface associated with address host. If
+ Bind to the specific interface associated with address host. If
an optional interface is specified, it is treated as a shortcut
- for --bind-dev dev. Note that a percent sign and interface
- device name are required for IPv6 link-local address literals.
+ for --bind-dev dev. Note that a percent sign and interface de-
+ vice name are required for IPv6 link-local address literals, in
+ order to set the link-local scope.
--bind-dev dev
- bind to the specified network interface. This option uses
- SO_BINDTODEVICE, and may require root permissions. (Available
- on Linux and possibly other systems.)
+ Bind to the specified network interface. This option uses
+ SO_BINDTODEVICE or IP_BOUND_IF, and may require root permissions.
+ (Available on Linux and possibly other systems.)
-V, --verbose
- give more detailed output
+ Produce more detailed output.
-J, --json
- output in JSON format
+ Output in JSON format instead of the default human-readable out-
+ put.
+
+ --json-stream
+ Output in line-delimited JSON format instead of the default hu-
+ man-readable output. This option overrides the --json option, if
+ that option was also specified.
+
+ --json-stream-full-output
+ Output in JSON format with JSON streams enabled. This flag only
+ takes effect if the --json-stream option was also specified.
--logfile file
- send output to a log file.
+ Send output to a log file.
--forceflush
- force flushing output at every interval. Used to avoid buffer-
+ Force flushing output at every interval. Used to avoid buffer-
ing when sending output to pipe.
--timestamps[=format]
- prepend a timestamp at the start of each output line. By
- default, timestamps have the format emitted by ctime(1).
- Optionally, = followed by a format specification can be passed
- to customize the timestamps, see strftime(3). If this optional
- format is given, the = must immediately follow the --timestamps
- option with no whitespace intervening.
+ Prepend a timestamp at the start of each output line. By de-
+ fault, timestamps have the format emitted by ctime(1). Option-
+ ally, = followed by a format specification can be passed to cus-
+ tomize the timestamps, see strftime(3). If this optional format
+ is given, the = must immediately follow the --timestamps option
+ with no whitespace intervening.
--rcv-timeout #
- set idle timeout for receiving data during active tests. The
- receiver will halt a test if no data is received from the sender
- for this number of ms (default to 12000 ms, or 2 minutes).
+ Set idle timeout for receiving data during active tests. The re-
+ ceiver will halt a test if no data is received from the sender
+ for this number of ms (default to 120000 ms, or 2 minutes).
--snd-timeout #
- set timeout for unacknowledged TCP data (on both test and con-
+ Set timeout for unacknowledged TCP data (on both test and con-
trol connections) This option can be used to force a faster test
- timeout in case of a network partition during a test. The
- required parameter is specified in ms, and defaults to the sys-
- tem settings. This functionality depends on the TCP_USER_TIME-
- OUT socket option, and will not work on systems that do not sup-
- port it.
+ timeout in case of a network partition during a test. The re-
+ quired parameter is specified in ms, and defaults to the system
+ settings. This functionality depends on the TCP_USER_TIMEOUT
+ socket option, and will not work on systems that do not support
+ it.
+
+ --use-pkcs1-padding
+ This option is only meaningful when using iperf3's authentica-
+ tion features. Versions of iperf3 prior to 3.17 used PCKS1
+ padding in the RSA-encrypted credentials, which was vulnerable
+ to a side-channel attack that could reveal a server's private
+ key. Beginning with iperf-3.17, OAEP padding is used, however
+ this is a breaking change that is not compatible with older
+ iperf3 versions. Use this option to preserve the less secure,
+ but more compatible, behavior.
+
+ -m, --mptcp
+ Use the MPTCP variant for the current protocol. This only ap-
+ plies to TCP and enables MPTCP usage.
-d, --debug
- emit debugging output. Primarily (perhaps exclusively) of use
+ Emit debugging output. Primarily (perhaps exclusively) of use
to developers.
-v, --version
- show version information and quit
+ Show version information and quit.
-h, --help
- show a help synopsis
+ Show a help synopsis.
SERVER SPECIFIC OPTIONS
-s, --server
- run in server mode
+ Run in server mode.
-D, --daemon
- run the server in background as a daemon
+ Run the server in background as a daemon.
-1, --one-off
- handle one client connection, then exit. If an idle time is
- set, the server will exit after that amount of time with no con-
- nection.
+ Handle (at most) one client connection, then exit. If an idle
+ time is set, the server will exit after that amount of time with
+ no connection.
--idle-timeout n
- restart the server after n seconds in case it gets stuck. In
+ Restart the server after n seconds in case it gets stuck. In
one-off mode, this is the number of seconds the server will wait
before exiting.
- --server-bitrate-limit n[KMGT]
- set a limit on the server side, which will cause a test to abort
- if the client specifies a test of more than n bits per second,
+ --server-max-duration
+ The maximum time, in seconds, that an iperf client can run
+ against the server. When the sum of the client's time and omit
+ values exceeds the max duration set by the server or the
+ client's time value is 0, the measurement is rejected.
+
+ --server-bitrate-limit n[KMGT][/n]
+ Set a limit on the server side, which will cause a test to abort
+ if the client specifies a test of more than n bits per second,
or if the average data sent or received by the client (including
- all data streams) is greater than n 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
+ all data streams) is greater than n bits per second. The de-
+ fault limit is 0, 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 / character and a number to the bitrate
specifier.
--rsa-private-key-path file
- path to the RSA private key (not password-protected) used to
- decrypt authentication credentials from the client (if built
- with OpenSSL support).
+ Path to the RSA private key (not password-protected) used to de-
+ crypt authentication credentials from the client (if built with
+ OpenSSL support).
--authorized-users-path file
- path to the configuration file containing authorized users cre-
+ Path to the configuration file containing authorized users cre-
dentials to run iperf tests (if built with OpenSSL support).
The file is a comma separated list of usernames and password
hashes; more information on the structure of the file can be
found in the EXAMPLES section.
- --time-skew-thresholdsecond seconds
- time skew threshold (in seconds) between the server and client
- during the authentication process.
+ --time-skew-threshold seconds
+ Specify the allowable time skew threshold (in seconds) between
+ the server and client during the authentication process.
CLIENT SPECIFIC OPTIONS
-c, --client host[%dev]
- run in client mode, connecting to the specified server. By
- default, a test consists of sending data from the client to the
+ Run in client mode, connecting to the specified server. By de-
+ fault, a test consists of sending data from the client to the
server, unless the -R flag is specified. If an optional inter-
face is specified, it is treated as a shortcut for --bind-dev
- dev. Note that a percent sign and interface device name are
- required for IPv6 link-local address literals.
+ dev. Note that a percent sign and interface device name are re-
+ quired for IPv6 link-local address literals.
- --sctp use SCTP rather than TCP (FreeBSD and Linux)
+ --sctp Use SCTP for tests rather than TCP (FreeBSD and Linux). Note
+ that TCP communication is still used for the control connection
+ between client and server.
-u, --udp
- use UDP rather than TCP
+ Use UDP for tests rather than TCP. Note that TCP communication
+ is still used for the control connection between client and
+ server.
--connect-timeout n
- set timeout for establishing the initial control connection to
- the server, in milliseconds. The default behavior is the oper-
- ating system's timeout for TCP connection establishment. Pro-
- viding a shorter value may speed up detection of a down iperf3
+ Set timeout for establishing the initial control connection to
+ the server, in milliseconds. The default behavior is the oper-
+ ating system's timeout for TCP connection establishment. Pro-
+ viding a shorter value may speed up detection of a down iperf3
server.
-b, --bitrate n[KMGT]
- set target bitrate to n bits/sec (default 1 Mbit/sec for UDP,
- unlimited for TCP/SCTP). If there are multiple streams (-P
- flag), the throughput limit is applied separately to each
- stream. You can also add a '/' and a number to the bitrate
- specifier. This is called "burst mode". It will send the given
- number of packets without pausing, even if that temporarily
- exceeds the specified throughput limit. Setting the target
- bitrate to 0 will disable bitrate limits (particularly useful
- for UDP tests). This throughput limit is implemented internally
- inside iperf3, and is available on all platforms. Compare with
+ Set target bitrate to n bits/sec (default 1 Mbit/sec for UDP,
+ unlimited for TCP/SCTP). If there are multiple streams (-P
+ flag), the throughput limit is applied separately to each
+ stream. You can also add a '/' and a number to the bitrate
+ specifier. This is called "burst mode". It will perform the
+ given number of sends without pausing, even if that temporarily
+ exceeds the specified throughput limit. Setting the target bi-
+ trate to 0 will disable bitrate limits (particularly useful for
+ UDP tests). This throughput limit is implemented internally in-
+ side iperf3, and is available on all platforms. Compare with
the --fq-rate flag. This option replaces the --bandwidth flag,
which is now deprecated but (at least for now) still accepted.
--pacing-timer n[KMGT]
- set pacing timer interval in microseconds (default 1000
- microseconds, or 1 ms). This controls iperf3's internal pacing
- timer for the -b/--bitrate option. The timer fires at the
- interval set by this parameter. Smaller values of the pacing
+ Set pacing timer interval in microseconds (default 1000 mi-
+ croseconds, or 1 ms). This controls iperf3's internal pacing
+ timer for the -b/--bitrate option. The timer fires at the in-
+ terval set by this parameter. Smaller values of the pacing
timer parameter smooth out the traffic emitted by iperf3, but
potentially at the cost of performance due to more frequent
timer processing.
--fq-rate n[KMGT]
Set a rate to be used with fair-queueing based socket-level pac-
- ing, in bits per second. This pacing (if specified) will be in
- addition to any pacing due to iperf3's internal throughput pac-
- ing (-b/--bitrate flag), and both can be specified for the same
- test. Only available on platforms supporting the SO_MAX_PAC-
- ING_RATE socket option (currently only Linux). The default is
+ ing, in bits per second. This pacing (if specified) will be in
+ addition to any pacing due to iperf3's internal throughput pac-
+ ing (-b/--bitrate flag), and both can be specified for the same
+ test. Only available on platforms supporting the SO_MAX_PAC-
+ ING_RATE socket option (currently only Linux). The default is
no fair-queueing based pacing.
--no-fq-socket-pacing
This option is deprecated and will be removed. It is equivalent
- to specifying --fq-rate=0.
+ to specifying --fq-rate=0 .
-t, --time n
- time in seconds to transmit for (default 10 secs)
+ Set the test duration in seconds (default 10 secs). The -t , -n
+ ", and" -k options are mutually exclusive.
-n, --bytes n[KMGT]
- number of bytes to transmit (instead of -t)
+ Set the number of bytes to transmit. The -t , -n ", and" -k op-
+ tions are mutually exclusive.
-k, --blockcount n[KMGT]
- number of blocks (packets) to transmit (instead of -t or -n)
+ Set the number of blocks (packets) to transmit. The -t , -n ",
+ and" -k options are mutually exclusive.
-l, --length n[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 sending size based on the path MTU; if
- that cannot be determined it uses 1460 bytes as a sending size.
- For SCTP tests, the default size is 64KB.
+ Set the length of the 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 sending size based on the
+ path MTU; if that cannot be determined it uses 1460 bytes as a
+ sending size. For SCTP tests, the default size is 64KB.
--cport port
- bind data streams to a specific client port (for TCP and UDP
- only, default is to use an ephemeral port)
+ Bind data streams to a specific TCP or UDP client port (for TCP
+ and UDP only, default is to use an ephemeral port).
-P, --parallel n
- number of parallel client streams to run. Note that iperf3 is
- single threaded, so if you are CPU bound, this will not yield
- higher throughput.
+ Set the number of parallel client streams to run. Beginning with
+ iperf-3.16, iperf3 will spawn off a separate thread for each
+ test stream. Using multiple streams may result in higher
+ throughput than a single stream, in cases where network through-
+ put is CPU-limited.
-R, --reverse
- reverse the direction of a test, so that the server sends data
- to the client
+ Reverse the direction of a test, so that the server sends data
+ to the client.
--bidir
- test in both directions (normal and reverse), with both the
+ Test in both directions (normal and reverse), with both the
client and server sending and receiving data simultaneously
-w, --window n[KMGT]
- set socket buffer size / window size. This value gets sent to
- the server and used on that side too; on both sides this option
- sets both the sending and receiving socket buffer sizes. This
- option can be used to set (indirectly) the maximum TCP window
- size. Note that on Linux systems, the effective maximum window
- size is approximately double what is specified by this option
- (this behavior is not a bug in iperf3 but a "feature" of the
- Linux kernel, as documented by tcp(7) and socket(7)).
+ Set t he socket buffer size / window size. This value gets sent
+ to the server and used on that side too; on both sides this op-
+ tion sets both the sending and receiving socket buffer sizes.
+ This option can be used to set (indirectly) the maximum TCP win-
+ dow size. Note that on Linux systems, the effective maximum
+ window size is approximately double what is specified by this
+ option. This behavior is not a bug in iperf3 but a feature of
+ the Linux kernel, as documented by tcp(7) and socket(7)).
-M, --set-mss n
- set TCP/SCTP maximum segment size (MTU - 40 bytes)
+ Set the TCP/SCTP maximum segment size (MTU - 40 bytes).
-N, --no-delay
- set TCP/SCTP no delay, disabling Nagle's Algorithm
+ Set the TCP/SCTP no delay option, disabling Nagle's Algorithm.
-4, --version4
- only use IPv4
+ Force the use of IPv4.
-6, --version6
- only use IPv6
+ Force the use of IPv6.
-S, --tos n
- set the IP type of service. The usual prefixes for octal and hex
- can be used, i.e. 52, 064 and 0x34 all specify the same value.
+ Set the IP type of service bits. The usual prefixes for octal
+ and hex can be used, i.e. 52, 064 and 0x34 all specify the same
+ value.
--dscp dscp
- set the IP DSCP bits. Both numeric and symbolic values are
- accepted. Numeric values can be specified in decimal, octal and
- hex (see --tos above). To set both the DSCP bits and the ECN
- bits, use --tos.
+ Set the IP DSCP bits. Both numeric and symbolic values are ac-
+ cepted. Numeric values can be specified in decimal, octal and
+ hex (see --tos above).
-L, --flowlabel n
- set the IPv6 flow label (currently only supported on Linux)
+ Set the IPv6 flow label (currently only supported on Linux).
-X, --xbind name
Bind SCTP associations to a specific subset of links using
sctp_bindx(3). The --B flag will be ignored if this flag is
specified. Normally SCTP will include the protocol addresses of
- all active links on the local host when setting up an associa-
- tion. Specifying at least one --X name will disable this behav-
- iour. This flag must be specified for each link to be included
- in the association, and is supported for both iperf servers and
+ all active links on the local host when setting up an associa-
+ tion. Specifying at least one --X name will disable this behav-
+ iour. This flag must be specified for each link to be included
+ in the association, and is supported for both iperf servers and
clients (the latter are supported by passing the first --X argu-
ment to bind(2)). Hostnames are accepted as arguments and are
- resolved using getaddrinfo(3). If the --4 or --6 flags are
- specified, names which do not resolve to addresses within the
+ resolved using getaddrinfo(3). If the --4 or --6 flags are also
+ specified, names which do not resolve to addresses within the
specified protocol family will be ignored.
--nstreams n
Set number of SCTP streams.
-Z, --zerocopy
- Use a "zero copy" method of sending data, such as sendfile(2),
+ Use a "zero copy" method of sending data, such as sendfile(2),
instead of the usual write(2).
+ --skip-rx-copy
+ Ignored received packet data, using the MSG_TRUNC flag to the
+ recv(2) system call.
+
-O, --omit n
- Perform pre-test for N seconds and omit the pre-test statistics,
+ Perform pre-test for n seconds and omit the pre-test statistics,
to skip past the TCP slow-start period.
-T, --title str
- Prefix every output line with this string.
+ Prefix every output line with the string str.
--extra-data str
- Specify an extra data string field to be included in JSON out-
+ Specify an extra data string field to be included in JSON out-
put.
-C, --congestion algo
- Set the congestion control algorithm (Linux and FreeBSD only).
- An older --linux-congestion synonym for this flag is accepted
+ Set the congestion control algorithm (Linux and FreeBSD only).
+ An older --linux-congestion synonym for this flag is accepted
but is deprecated.
--get-server-output
Get the output from the server. The output format is determined
by the server (in particular, if the server was invoked with the
- --json flag, the output will be in JSON format, otherwise it
- will be in human-readable format). If the client is run with
- --json, the server output is included in a JSON object; other-
- wise it is appended at the bottom of the human-readable output.
+ --json flag, the output will be in JSON format, otherwise it
+ will be in human-readable format). If the client is run with
+ --json, the server output is included in a JSON object; other-
+ wise it is appended at the bottom of the human-readable output.
+ Note that the server output is available only if the test com-
+ pletes, not if it is interrupted.
--udp-counters-64bit
Use 64-bit counters in UDP test packets. The use of this option
- can help prevent counter overflows during long or high-bitrate
- UDP tests. Both client and server need to be running at least
- version 3.1 for this option to work. It may become the default
+ can help prevent counter overflows during long or high-bitrate
+ UDP tests. Both client and server need to be running at least
+ version 3.1 for this option to work. It may become the default
behavior at some point in the future.
--repeating-payload
- Use repeating pattern in payload, instead of random bytes. The
- same payload is used in iperf2 (ASCII '0..9' repeating). It
- might help to test and reveal problems in networking gear with
- hardware compression (including some WiFi access points), where
- iperf2 and iperf3 perform differently, just based on payload
- entropy.
+ Use repeating pattern in payload, instead of random bytes. The
+ same payload is used in iperf2 (ASCII '0..9' repeating). It
+ might help to test and reveal problems in networking gear with
+ hardware compression (including some WiFi access points), where
+ iperf2 and iperf3 perform differently, just based on payload en-
+ tropy.
--dont-fragment
Set the IPv4 Don't Fragment (DF) bit on outgoing packets. Only
applicable to tests doing UDP over IPv4.
--username username
- username to use for authentication to the iperf server (if built
- with OpenSSL support). The password will be prompted for inter-
- actively when the test is run. Note, the password to use can
+ Specify username to use for authentication to the iperf server
+ (if built with OpenSSL support). The password will be prompted
+ for interactively when the test is run. Note the password can
also be specified via the IPERF3_PASSWORD environment variable.
If this variable is present, the password prompt will be
skipped.
--rsa-public-key-path file
- path to the RSA public key used to encrypt authentication cre-
- dentials (if built with OpenSSL support)
+ Set path to the RSA public key used to encrypt authentication
+ credentials (if built with OpenSSL support).
EXAMPLES
Authentication - RSA Keypair
The authentication feature of iperf3 requires an RSA public keypair.
The public key is used to encrypt the authentication token containing
- the user credentials, while the private key is used to decrypt the
- authentication token. The private key must be in PEM format and addi-
+ the user credentials, while the private key is used to decrypt the au-
+ thentication token. The private key must be in PEM format and addi-
tionally must not have a password set. The public key must be in PEM
format and use SubjectPrefixKeyInfo encoding. An example of a set of
UNIX/Linux commands using OpenSSL to generate a correctly-formed key-
pair follows:
- > openssl genrsa -des3 -out private.pem 2048
- > openssl rsa -in private.pem -outform PEM -pubout -out public.pem
- > openssl rsa -in private.pem -out private_not_protected.pem -out-
- form PEM
+ > openssl genrsa -des3 -out private.pem 2048
+ > openssl rsa -in private.pem -outform PEM -pubout -out pub-
+ lic.pem
+ > openssl rsa -in private.pem -out private_not_protected.pem \
+ -outform PEM
After these commands, the public key will be contained in the file pub-
lic.pem and the private key will be contained in the file pri-
@@ -490,16 +537,16 @@ the executable.
character). An example of commands to generate the password hash on a
UNIX/Linux system is given below:
- > S_USER=mario S_PASSWD=rossi
- > echo -n "{$S_USER}$S_PASSWD" | sha256sum | awk '{ print $1 }'
+ > S_USER=mario S_PASSWD=rossi
+ > echo -n "{$S_USER}$S_PASSWD" | sha256sum | awk '{ print $1 }'
An example of a password file (with an entry corresponding to the above
username and password) is given below:
- > cat credentials.csv
- # file format: username,sha256
- mario,bf7a49a846d44b454a5d11e7acfaf13d138bbe0b7483aa3e050879700572709b
-
+ > cat credentials.csv
+ # file format: username,sha256
+ mario,bf7a49a846d44b454a5d11e7ac-
+ faf13d138bbe0b7483aa3e050879700572709b
AUTHORS
A list of the contributors to iperf3 can be found within the documenta-
@@ -509,10 +556,7 @@ the executable.
SEE ALSO
libiperf(3), https://software.es.net/iperf
-
-
- ESnet September 2022 IPERF3(1)
-
+ ESnet November 2025 IPERF3(1)
The iperf3 manual page will typically be installed in manual
section 1.
diff --git a/docs/news.rst b/docs/news.rst
index be69696f2..bc8949d3a 100644
--- a/docs/news.rst
+++ b/docs/news.rst
@@ -1,8 +1,132 @@
iperf3 Project News
===================
-2023-02-16: iperf-3.13 released
+2026-04-09: iperf-3.21 released
+--------------------------------
+
+| URL: https://downloads.es.net/pub/iperf/iperf-3.21.tar.gz
+| SHA256: ``656e4405ebd620121de7ceca3eaf43a88f79ea1b857d041a6a0b1314801acdd8``
+
+iperf-3.21 includes support for GSO and GRO under Linux, improves
+feature parity for macOS, and adds a number of minor bugs and
+enhancements. More details on the changes can be found in the release
+notes.
+
+2025-11-14: iperf-3.20 released
+--------------------------------
+
+| URL: https://downloads.es.net/pub/iperf/iperf-3.20.tar.gz
+| SHA256: ``3acc572d1ecca4e0b20359c7bf0132ddc80d982efeee20c86f6726a9a6094388``
+
+iperf-3.20 fixes a number of bugs and also adds some minor
+enhancements. More details on the changes can be found in the release
+notes.
+
+2025-07-25: iperf-3.19.1 released
+----------------------------------
+
+| URL: https://downloads.es.net/pub/iperf/iperf-3.19.1.tar.gz
+| SHA256: ``dc63f89ec581ea99f8b558d8eb35109de06383010db5a1906c208a562ba0c270``
+
+iperf-3.19.1 is a security fix release to address three issues
+reported by Han Lee of Apple Information Security. More information
+can be found in the release notes.
+
+2025-05-16: iperf-3.19 released
+--------------------------------
+
+| URL: https://downloads.es.net/pub/iperf/iperf-3.19.tar.gz
+| SHA256: ``040161da1555ec7411a9d81191049830ef37717d429a94ee6cf0842618e0e29c``
+
+iperf-3.19 includes support for MP-TCPv1 under Linux, keepalives on
+the control connection, support for the MSG_TRUNC receive option, and
+a number of minor bug fixes. The release notes contain more
+information on the changes.
+
+
+2024-12-13: iperf-3.18 released
+--------------------------------
+
+| URL: https://downloads.es.net/pub/iperf/iperf-3.18.tar.gz
+| SHA256: ``c0618175514331e766522500e20c94bfb293b4424eb27d7207fb427b88d20bab``
+
+iperf-3.18 includes fixes for several bugs, including one that could
+cause an iperf3 server to crash if fed malformed data on the control
+connection. The release notes contain more information on the bug and
+the fix.
+
+
+2024-05-13: iperf-3.17.1 released
----------------------------------
+
+| URL: https://downloads.es.net/pub/iperf/iperf-3.17.1.tar.gz
+| SHA256: ``84404ca8431b595e86c473d8f23d8bb102810001f15feaf610effd3b318788aa``
+
+iperf-3.17.1 fixes some issues with version numbers in various
+places. It is otherwise identical to iperf-3.17.
+
+
+2024-05-10: iperf-3.17 released
+--------------------------------
+| URL: https://downloads.es.net/pub/iperf/iperf-3.17.tar.gz
+| SHA256: ``077ede831b11b733ecf8b273abd97f9630fd7448d3ec1eaa789f396d82c8c943``
+
+iperf 3.17 contains a fix for a minor security vulnerability related
+to iperf3's authentication feature and its use of RSA-encrypted
+credentials. The release notes contain more
+details on this breaking change, and how to revert to older, less
+secure behavior if needed for backward compatibility with older
+versions of iperf3.
+
+This version also contains a new streaming JSON output format (enabled
+with the --json-stream) option, and a number of other bug fixes.
+
+2023-12-01: iperf-3.16 released
+--------------------------------
+| URL: https://downloads.es.net/pub/iperf/iperf-3.16.tar.gz
+| SHA256: ``cc740c6bbea104398cc3e466befc515a25896ec85e44a662d5f4a767b9cf713e``
+
+iperf 3.16 uses multiple threads to serve parallel tests for improved
+throughput on high-speed links. It also includes support for
+OpenSSL 3. More details are provided in the release notes.
+
+Older News
+----------
+
+2023-09-14: iperf-3.15 released
+................................
+
+| URL: https://downloads.es.net/pub/iperf/iperf-3.15.tar.gz
+| SHA256: ``bdb77c11f72bce90214883159577fa24412013e62b2083cf5f54391d79b1d8ff``
+
+iperf 3.15 fixes that could cause an iperf3 server process to hang
+waiting for input on the control connection. For more information,
+please see:
+
+https://downloads.es.net/pub/iperf/esnet-secadv-2023-0002.txt.asc
+
+This version of iperf3 also includes several other minor bug fixes,
+which are summarized in the release notes.
+
+2023-07-07: iperf-3.14 released
+................................
+
+| URL: https://downloads.es.net/pub/iperf/iperf-3.14.tar.gz
+| SHA256: ``723fcc430a027bc6952628fa2a3ac77584a1d0bd328275e573fc9b206c155004``
+
+iperf 3.14 fixes a memory allocation hazard that allowed a remote user
+to crash an iperf3 process (server or client).
+
+More information on this specific fix can be found at:
+
+https://downloads.es.net/pub/iperf/esnet-secadv-2023-0001.txt.asc
+
+This version of iperf3 also includes a number of minor bug fixes,
+which are summarized in the release notes.
+
+2023-02-16: iperf-3.13 released
+................................
+
| URL: https://downloads.es.net/pub/iperf/iperf-3.13.tar.gz
| SHA256: ``bee427aeb13d6a2ee22073f23261f63712d82befaa83ac8cb4db5da4c2bdc865``
@@ -10,7 +134,8 @@ iperf 3.13 is primarily a bugfix release.
2022-09-30: iperf-3.12 released
-----------------------------------
+................................
+
| URL: https://downloads.es.net/pub/iperf/iperf-3.12.tar.gz
| SHA256: ``72034ecfb6a7d6d67e384e19fb6efff3236ca4f7ed4c518d7db649c447e1ffd6``
@@ -19,7 +144,8 @@ updated version of cJSON and adds a few new features.
2022-01-28: iperf-3.11 released
-----------------------------------
+................................
+
| URL: https://downloads.es.net/pub/iperf/iperf-3.11.tar.gz
| SHA256: ``de8cb409fad61a0574f4cb07eb19ce1159707403ac2dc01b5d175e91240b7e5f``
@@ -28,7 +154,7 @@ Discussions are now supported.
2021-06-02: iperf-3.10.1 released
-----------------------------------
+..................................
| URL: https://downloads.es.net/pub/iperf/iperf-3.10.1.tar.gz
| SHA256: ``03bc9760cc54a245191d46bfc8edaf8a4750f0e87abca6764486972044d6715a iperf-3.10.1.tar.gz``
@@ -38,7 +164,7 @@ make not work correctly in some circumstances. It is functionally
identical to iperf 3.10.
2021-05-26: iperf-3.10 released
---------------------------------
+................................
| URL: https://downloads.es.net/pub/iperf/iperf-3.10.tar.gz
| SHA256: ``4390982928542256c17d6dd1f56eede9092649ebfd8a97c8cecfad12d238ad57 iperf-3.10.tar.gz``
@@ -49,7 +175,7 @@ been added (``--time-skew-threshold``, ``--bind-dev``,
these new features can be found in the release notes.
2020-08-17: iperf-3.9 released
----------------------------------
+...............................
| URL: https://downloads.es.net/pub/iperf/iperf-3.9.tar.gz
| SHA256: ``24b63a26382325f759f11d421779a937b63ca1bc17c44587d2fcfedab60ac038 iperf-3.9.tar.gz``
@@ -61,7 +187,7 @@ to enforce a maximum throughput rate. More information on these new
features can be found in the release notes.
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``
@@ -70,7 +196,7 @@ 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``
@@ -79,7 +205,7 @@ 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``
@@ -93,7 +219,7 @@ omitted from the manual page. This will be fixed in a future
release.
2018-06-25: iperf-3.6 released
--------------------------------
+...............................
| URL: https://downloads.es.net/pub/iperf/iperf-3.6.tar.gz
| SHA256: ``de5d51e46dc460cc590fb4d44f95e7cad54b74fea1eba7d6ebd6f8887d75946e iperf-3.6.tar.gz``
@@ -102,54 +228,54 @@ iperf 3.6 adds the ``--extra-data`` and ``--repeating-payload``
options and fixes some minor bugs.
2018-03-02: iperf-3.5 released
--------------------------------
+...............................
-| URL: http://downloads.es.net/pub/iperf/iperf-3.5.tar.gz
+| URL: https://downloads.es.net/pub/iperf/iperf-3.5.tar.gz
| SHA256: ``539bd9ecdca1b8c1157ff85b70ed09b3c75242e69886fc16b54883b399f72cd5 iperf-3.5.tar.gz``
iperf 3.5 fixes a bug that could over-count data transfers (and hence
measured bitrate).
2018-02-14: iperf-3.4 released
--------------------------------
+...............................
-| URL: http://downloads.es.net/pub/iperf/iperf-3.4.tar.gz
+| URL: https://downloads.es.net/pub/iperf/iperf-3.4.tar.gz
| SHA256: ``71528332d751df85e046d1944d9a0269773cadd6e51840aecdeed30925f79111 iperf-3.4.tar.gz``
iperf 3.4 fixes a number of minor bugs and adds a few enhancements.
2017-10-31: iperf-3.3 released
--------------------------------
+...............................
-| URL: http://downloads.es.net/pub/iperf/iperf-3.3.tar.gz
+| URL: https://downloads.es.net/pub/iperf/iperf-3.3.tar.gz
| SHA256: ``6f596271251056bffc11bbb8f17d4244ad9a7d4a317c2459fdbb853ae51284d8 iperf-3.3.tar.gz``
New minor release of iperf 3.3, fixing a number of minor bugs.
2017-06-26: iperf-3.2 released
--------------------------------
+...............................
-| URL: http://downloads.es.net/pub/iperf/iperf-3.2.tar.gz
+| URL: https://downloads.es.net/pub/iperf/iperf-3.2.tar.gz
| SHA256: ``f207b36f861485845dbdf09f909c62f3d2222a3cf3d2682095aede8213cd9c1d iperf-3.2.tar.gz``
New minor release of iperf 3.2, with new features, bugfixes, and enhancements.
2017-06-06: iperf3 update, June 2017
---------------------------------------
+.....................................
https://raw.githubusercontent.com/esnet/iperf/master/docs/2017-06-06.txt
2017-04-27: iperf3 update, April 2017
---------------------------------------
+......................................
https://raw.githubusercontent.com/esnet/iperf/master/docs/2017-04-27.txt
2017-03-06: iperf-3.1.7 released
----------------------------------
+.................................
-| URL: http://downloads.es.net/pub/iperf/iperf-3.1.7.tar.gz
+| URL: https://downloads.es.net/pub/iperf/iperf-3.1.7.tar.gz
| SHA256: ``a4ef73406fe92250602b8da2ae89ec53211f805df97a1d1d629db5a14043734f iperf-3.1.7.tar.gz``
This version of iperf3 contains two documentation fixes, but is
@@ -157,9 +283,9 @@ otherwise identical to the prior release.
2017-02-02: iperf-3.1.6 released
----------------------------------
+.................................
-| URL: http://downloads.es.net/pub/iperf/iperf-3.1.6.tar.gz
+| URL: https://downloads.es.net/pub/iperf/iperf-3.1.6.tar.gz
| SHA256: ``70f0c72d9e60c6ecb2c478ed17e4fd81d3b827d57896fee43bcd0c53abccb29d iperf-3.1.6.tar.gz``
This version of iperf3 contains two minor fixes. Notably, one of them
@@ -167,9 +293,9 @@ unbreaks JSON output with UDP tests.
2017-01-12: iperf-3.1.5 released
----------------------------------
+.................................
-| URL: http://downloads.es.net/pub/iperf/iperf-3.1.5.tar.gz
+| URL: https://downloads.es.net/pub/iperf/iperf-3.1.5.tar.gz
| SHA256: ``6e1a6200cd38baeab58ef0d7b8769e7aa6410c3a3168e65ea8277a4de79e5500 iperf-3.1.5.tar.gz``
This version of iperf3 makes some improvements to the fair-queue-based
@@ -179,9 +305,9 @@ review the release notes for this version.
2016-10-31: iperf-3.1.4 released
----------------------------------
+.................................
-| URL: http://downloads.es.net/pub/iperf/iperf-3.1.4.tar.gz
+| URL: https://downloads.es.net/pub/iperf/iperf-3.1.4.tar.gz
| SHA256: ``db61d70ac62003ebe0bf15496bd8c4b3c4b728578a44d0a1a88fcf8afc0e8f76 iperf-3.1.4.tar.gz``
This release fixes a few minor bugs, including a
@@ -190,12 +316,12 @@ cjson.
2016-06-08: Security Issue: iperf-3.1.3, iperf-3.0.12 released
-----------------------------------------------------------------
+................................................................
-| URL: http://downloads.es.net/pub/iperf/iperf-3.1.3.tar.gz
+| URL: https://downloads.es.net/pub/iperf/iperf-3.1.3.tar.gz
| SHA256: ``60d8db69b1d74a64d78566c2317c373a85fef691b8d277737ee5d29f448595bf iperf-3.1.3.tar.gz``
-| URL: http://downloads.es.net/pub/iperf/iperf-3.0.12.tar.gz
+| URL: https://downloads.es.net/pub/iperf/iperf-3.0.12.tar.gz
| SHA256: ``9393d646e4e616f0cd7864bc8ceacc379f5d36b08003a3d8d65cd7c99d15daec iperf-3.0.12.tar.gz``
These releases address a security issue that could cause a crash of an
@@ -213,21 +339,18 @@ distributions), as well as several other fixes.
2016-02-01: iperf-3.1.2 released
----------------------------------
+.................................
-| URL: http://downloads.es.net/pub/iperf/iperf-3.1.2.tar.gz
+| URL: https://downloads.es.net/pub/iperf/iperf-3.1.2.tar.gz
| SHA256: ``f9dbdb99f869c077d14bc1de78675f5e4b8d1bf78dc92381e96c3eb5b1fd7d86 iperf-3.1.2.tar.gz``
This release fixes a couple of minor bugs, including one that results
in invalid JSON being emitted for UDP tests.
-Older News
-----------
-
2015-11-19: iperf-3.1.1 released
.................................
-| URL: http://downloads.es.net/pub/iperf/iperf-3.1.1.tar.gz
+| URL: https://downloads.es.net/pub/iperf/iperf-3.1.1.tar.gz
| SHA256: ``62f7c64eafe19046ba974b3ef2d962a5597194d6fbbddde328a15a5e74110564 iperf-3.1.1.tar.gz``
This release fixes a few minor bugs.
@@ -252,7 +375,7 @@ and/or supporting the user base.
2015-10-16: iperf-3.1 released
...............................
-| URL: http://downloads.es.net/pub/iperf/iperf-3.1.tar.gz
+| URL: https://downloads.es.net/pub/iperf/iperf-3.1.tar.gz
| SHA256: ``4385a32ece25cb09d4606b4c99316356b3d2cb03b318aa056b99cdb91c5ce656 iperf-3.1.tar.gz``
This release adds support for SCTP on supported platforms, better
@@ -263,7 +386,7 @@ platforms, and a number of bug fixes.
2015-01-09: iperf-3.0.11 released
..................................
-| URL: http://downloads.es.net/pub/iperf/iperf-3.0.11.tar.gz
+| URL: https://downloads.es.net/pub/iperf/iperf-3.0.11.tar.gz
| SHA256: ``e01db5be6f47f67c987463095fe4f5b8b9ff891fb92c39104d042ad8fde97f6e iperf-3.0.11.tar.gz``
This maintenance release adds a -1 flag to make the iperf3 execute a
@@ -273,7 +396,7 @@ other bugs are also fixed.
2014-12-16: iperf-3.0.10 released
..................................
-| URL: http://downloads.es.net/pub/iperf/iperf-3.0.10.tar.gz
+| URL: https://downloads.es.net/pub/iperf/iperf-3.0.10.tar.gz
| SHA256: ``a113442967cf0981b0b2d538be7c88903b2fb0f87b0d281384e41b462e33059d iperf-3.0.10.tar.gz``
This maintenance release fixes building on MacOS X Yosemite, as well
@@ -282,7 +405,7 @@ as making the -w option work correctly with UDP tests.
2014-10-14: iperf-3.0.9 released
.................................
-| URL: http://downloads.es.net/pub/iperf/iperf-3.0.9.tar.gz
+| URL: https://downloads.es.net/pub/iperf/iperf-3.0.9.tar.gz
| SHA256: ``40249a2b30d26b937350b969bcb19f88e1beb356f886ed31422b554bac692459 iperf-3.0.9.tar.gz``
This maintenance release fixes an issue for a situation in which
@@ -295,7 +418,7 @@ when interrupted.
2014-09-30: iperf-3.0.8 released
.................................
-| URL: http://downloads.es.net/pub/iperf/iperf-3.0.8.tar.gz
+| URL: https://downloads.es.net/pub/iperf/iperf-3.0.8.tar.gz
| SHA256: ``81b8d91159862896c57f9b90a006e8b5dc22bd94175d97bd0db50b0ae2c1a78e iperf-3.0.8.tar.gz``
This maintenance release is functionally identical to 3.0.7. It
@@ -305,7 +428,7 @@ incorporates updated license verbage and a minor compilation fix.
2014-08-28: iperf-3.0.7 released
.................................
-| URL: http://downloads.es.net/pub/iperf/iperf-3.0.7.tar.gz
+| URL: https://downloads.es.net/pub/iperf/iperf-3.0.7.tar.gz
| SHA256: ``49510e886f9e876cd73dcd80414bfb8c49b147c82125585e09c2a6e92369d3f2 iperf-3.0.7.tar.gz``
This maintenance release fixes several minor bugs. Of particular
@@ -320,7 +443,7 @@ note:
2014-07-28: iperf-3.0.6 released
.................................
-| URL: http://downloads.es.net/pub/iperf/iperf-3.0.6.tar.gz
+| URL: https://downloads.es.net/pub/iperf/iperf-3.0.6.tar.gz
| SHA256: ``3c5909c9b286b6503ffa141a94cfc588915d6e67f2aa732b08df0af73e21938 iperf-3.0.6.tar.gz``
This maintenance release includes the following bug fixes:
@@ -340,7 +463,7 @@ the source distribution.
iperf3 project documentation can now be found at:
-| URL: http://software.es.net/iperf/
+| URL: https://software.es.net/iperf/
This is a GitHub Pages site. In an ongoing series of steps, content
will be migrated from the iperf3 wiki to GitHub Pages.
@@ -348,7 +471,7 @@ will be migrated from the iperf3 wiki to GitHub Pages.
2014-06-16: iperf-3.0.5 released
.................................
-| URL: http://downloads.es.net/pub/iperf/iperf-3.0.5.tar.gz
+| URL: https://downloads.es.net/pub/iperf/iperf-3.0.5.tar.gz
| SHA256: ``e1e1989985b17a4c03b0fa207004ad164b137e37ab0682fecbf5e93bcaa920a6 iperf-3.0.5.tar.gz``
This is the third maintenance release of iperf 3, with few more
@@ -373,7 +496,7 @@ officially released.
iperf3 downloads are now hosted on a new server at ESnet:
-| URL: http://downloads.es.net/pub/iperf/
+| URL: https://downloads.es.net/pub/iperf/
Going forward, new releases will be made available in this directory.
Older releases will, at least for now, continue to also be available
@@ -382,7 +505,7 @@ at the previous location.
2014-03-26: iperf-3.0.3 released
.................................
-| URL: http://stats.es.net/software/iperf-3.0.3.tar.gz
+| URL: https://downloads.es.net/pub/iperf/iperf-3.0.3.tar.gz
| SHA256: ``79daf3e5e5c933b2fc4843d6d21c98d741fe39b33ac05bd7a11c50d321a2f59d iperf-3.0.3.tar.gz``
This is the second maintenance release of iperf 3.0, containing a few bug fixes and enhancements, notably:
@@ -401,7 +524,7 @@ file in the source distribution.
2014-03-10: iperf-3.0.2 released
.................................
-| URL: http://stats.es.net/software/iperf-3.0.2.tar.gz
+| URL: https://downloads.es.net/pub/iperf/iperf-3.0.2.tar.gz
| SHA256: ``3c379360bf40e6ac91dfc508cb43fefafb4739c651d9a8d905a30ec99095b282 iperf-3.0.2.tar.gz``
**Note:** Due to a mistake in the release process, the distribution tarball referred to above is actually not compressed, despite its ``.tar.gz`` extension. Instead it is an uncompressed tar archive. The file checksum is correct, as are the file contents.
@@ -431,7 +554,7 @@ https://github.com/esnet/iperf
2014-01-10: iperf-3.0.1 released
.................................
-| URL: http://stats.es.net/software/iperf-3.0.1.tar.gz
+| URL: https://downloads.es.net/pub/iperf/iperf-3.0.1.tar.gz
| SHA256: ``32b419ef634dd7670328c3cecc158babf7d706bd4b3d248cf95965528a20e614 iperf-3.0.1.tar.gz``
During development, there were various distributions of the source
diff --git a/docs/obtaining.rst b/docs/obtaining.rst
index 8a83cda7b..3414822f5 100644
--- a/docs/obtaining.rst
+++ b/docs/obtaining.rst
@@ -16,7 +16,7 @@ of binary packages for various operating systems and distributions:
* Fedora / RedHat Linux / CentOS / Rocky: `iperf3
`_ and
`iperf3-devel
- `_ in Fedora
19 and 20 and in Fedora EPEL 5, 6, and 7. iperf3 is included as a
part of RedHat Enterprise Linux 7.4 and later (as well as CentOS 7.4
and later, and all versions of Rocky Linux), and can generally be
@@ -31,6 +31,8 @@ of binary packages for various operating systems and distributions:
locations, including ``_
(`discussion thread
`_).
+* Android: iperf3 binaries for Android can be found in several
+ locations, including ``_.
Source Distributions
--------------------
@@ -72,7 +74,7 @@ GitHub using:
``git clone https://github.com/esnet/iperf.git``
-Primary development for iperf3 takes place on CentOS 7 Linux, FreeBSD 11,
-and macOS 10.12. At this time, these are the only officially supported
+Primary development for iperf3 takes place on Ubuntu Linux, FreeBSD,
+and macOS. At this time, these are the only officially supported
platforms, however there have been some reports of success with
NetBSD, OpenBSD, Windows, Solaris, Android, and iOS.
diff --git a/examples/Makefile.in b/examples/Makefile.in
index 93898c070..448884043 100644
--- a/examples/Makefile.in
+++ b/examples/Makefile.in
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.16.5 from Makefile.am.
+# Makefile.in generated by automake 1.18.1 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994-2021 Free Software Foundation, Inc.
+# Copyright (C) 1994-2025 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -70,6 +70,8 @@ am__make_running_with_option = \
test $$has_opt = yes
am__make_dryrun = (target_option=n; $(am__make_running_with_option))
am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+am__rm_f = rm -f $(am__rm_f_notfound)
+am__rm_rf = rm -rf $(am__rm_f_notfound)
pkgdatadir = $(datadir)/@PACKAGE@
pkgincludedir = $(includedir)/@PACKAGE@
pkglibdir = $(libdir)/@PACKAGE@
@@ -92,6 +94,7 @@ noinst_PROGRAMS = mic$(EXEEXT) mis$(EXEEXT)
subdir = examples
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = $(top_srcdir)/config/ax_check_openssl.m4 \
+ $(top_srcdir)/config/ax_pthread.m4 \
$(top_srcdir)/config/iperf_config_static_bin.m4 \
$(top_srcdir)/configure.ac
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
@@ -191,6 +194,7 @@ AWK = @AWK@
CC = @CC@
CCDEPMODE = @CCDEPMODE@
CFLAGS = @CFLAGS@
+CPP = @CPP@
CPPFLAGS = @CPPFLAGS@
CSCOPE = @CSCOPE@
CTAGS = @CTAGS@
@@ -215,6 +219,7 @@ INSTALL_PROGRAM = @INSTALL_PROGRAM@
INSTALL_SCRIPT = @INSTALL_SCRIPT@
INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
LD = @LD@
+LDCONFIG = @LDCONFIG@
LDFLAGS = @LDFLAGS@
LIBOBJS = @LIBOBJS@
LIBS = @LIBS@
@@ -245,6 +250,10 @@ PACKAGE_URL = @PACKAGE_URL@
PACKAGE_VERSION = @PACKAGE_VERSION@
PATH_SEPARATOR = @PATH_SEPARATOR@
PKG_CONFIG = @PKG_CONFIG@
+PTHREAD_CC = @PTHREAD_CC@
+PTHREAD_CFLAGS = @PTHREAD_CFLAGS@
+PTHREAD_CXX = @PTHREAD_CXX@
+PTHREAD_LIBS = @PTHREAD_LIBS@
RANLIB = @RANLIB@
SED = @SED@
SET_MAKE = @SET_MAKE@
@@ -261,8 +270,11 @@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
am__include = @am__include@
am__leading_dot = @am__leading_dot@
am__quote = @am__quote@
+am__rm_f_notfound = @am__rm_f_notfound@
am__tar = @am__tar@
am__untar = @am__untar@
+am__xargs_n = @am__xargs_n@
+ax_pthread_config = @ax_pthread_config@
bindir = @bindir@
build = @build@
build_alias = @build_alias@
@@ -348,13 +360,8 @@ $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
$(am__aclocal_m4_deps):
clean-noinstPROGRAMS:
- @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \
- echo " rm -f" $$list; \
- rm -f $$list || exit $$?; \
- test -n "$(EXEEXT)" || exit 0; \
- list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
- echo " rm -f" $$list; \
- rm -f $$list
+ $(am__rm_f) $(noinst_PROGRAMS)
+ test -z "$(EXEEXT)" || $(am__rm_f) $(noinst_PROGRAMS:$(EXEEXT)=)
mic$(EXEEXT): $(mic_OBJECTS) $(mic_DEPENDENCIES) $(EXTRA_mic_DEPENDENCIES)
@rm -f mic$(EXEEXT)
@@ -375,7 +382,7 @@ distclean-compile:
$(am__depfiles_remade):
@$(MKDIR_P) $(@D)
- @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+ @: >>$@
am--depfiles: $(am__depfiles_remade)
@@ -485,6 +492,7 @@ cscopelist-am: $(am__tagged_files)
distclean-tags:
-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
distdir: $(BUILT_SOURCES)
$(MAKE) $(AM_MAKEFLAGS) distdir-am
@@ -546,8 +554,8 @@ mostlyclean-generic:
clean-generic:
distclean-generic:
- -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
- -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+ -$(am__rm_f) $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || $(am__rm_f) $(CONFIG_CLEAN_VPATH_FILES)
maintainer-clean-generic:
@echo "This command is intended for maintainers to use"
@@ -558,7 +566,7 @@ clean-am: clean-generic clean-libtool clean-noinstPROGRAMS \
mostlyclean-am
distclean: distclean-am
- -rm -f ./$(DEPDIR)/mic-mic.Po
+ -rm -f ./$(DEPDIR)/mic-mic.Po
-rm -f ./$(DEPDIR)/mis-mis.Po
-rm -f Makefile
distclean-am: clean-am distclean-compile distclean-generic \
@@ -605,7 +613,7 @@ install-ps-am:
installcheck-am:
maintainer-clean: maintainer-clean-am
- -rm -f ./$(DEPDIR)/mic-mic.Po
+ -rm -f ./$(DEPDIR)/mic-mic.Po
-rm -f ./$(DEPDIR)/mis-mis.Po
-rm -f Makefile
maintainer-clean-am: distclean-am maintainer-clean-generic
@@ -647,3 +655,10 @@ uninstall-am:
# Tell versions [3.59,3.63) of GNU make to not export all variables.
# Otherwise a system limit (for SysV at least) may be exceeded.
.NOEXPORT:
+
+# Tell GNU make to disable its built-in pattern rules.
+%:: %,v
+%:: RCS/%,v
+%:: RCS/%
+%:: s.%
+%:: SCCS/s.%
diff --git a/examples/mic.c b/examples/mic.c
index 17fd7b2f9..6e2403f60 100644
--- a/examples/mic.c
+++ b/examples/mic.c
@@ -4,9 +4,7 @@
#include
#include
#include
-#ifdef HAVE_STDINT_H
#include
-#endif
#include
diff --git a/examples/mis.c b/examples/mis.c
index 8090c13d9..315b9e8ed 100644
--- a/examples/mis.c
+++ b/examples/mis.c
@@ -4,9 +4,7 @@
#include
#include
#include
-#ifdef HAVE_STDINT_H
#include
-#endif
#include
diff --git a/make_release b/make_release
index 54c43f0b9..ef805808f 100755
--- a/make_release
+++ b/make_release
@@ -21,7 +21,7 @@ echo dirname $dirname
do_tag ()
{
- git tag -s -m "tagging $tag" "$tag"
+ git tag -m "tagging $tag" "$tag"
}
do_tar ()
diff --git a/src/Makefile.am b/src/Makefile.am
index 11d3e175d..f2ae68335 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -33,6 +33,8 @@ libiperf_la_SOURCES = \
iperf_util.h \
iperf_time.c \
iperf_time.h \
+ iperf_pthread.c \
+ iperf_pthread.h \
dscp.c \
net.c \
net.h \
@@ -100,3 +102,6 @@ TESTS = \
t_auth
dist_man_MANS = iperf3.1 libiperf.3
+
+install-exec-hook:
+ if test -n "$(LDCONFIG)"; then $(LDCONFIG) || true; fi
diff --git a/src/Makefile.in b/src/Makefile.in
index 91c9b156e..8e6d82ffc 100644
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.16.5 from Makefile.am.
+# Makefile.in generated by automake 1.18.1 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994-2021 Free Software Foundation, Inc.
+# Copyright (C) 1994-2025 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -72,6 +72,8 @@ am__make_running_with_option = \
test $$has_opt = yes
am__make_dryrun = (target_option=n; $(am__make_running_with_option))
am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+am__rm_f = rm -f $(am__rm_f_notfound)
+am__rm_rf = rm -rf $(am__rm_f_notfound)
pkgdatadir = $(datadir)/@PACKAGE@
pkgincludedir = $(includedir)/@PACKAGE@
pkglibdir = $(libdir)/@PACKAGE@
@@ -103,6 +105,7 @@ TESTS = t_timer$(EXEEXT) t_units$(EXEEXT) t_uuid$(EXEEXT) \
subdir = src
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = $(top_srcdir)/config/ax_check_openssl.m4 \
+ $(top_srcdir)/config/ax_pthread.m4 \
$(top_srcdir)/config/iperf_config_static_bin.m4 \
$(top_srcdir)/configure.ac
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
@@ -139,18 +142,17 @@ am__base_list = \
sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
am__uninstall_files_from_dir = { \
- test -z "$$files" \
- || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
- || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
- $(am__cd) "$$dir" && rm -f $$files; }; \
+ { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && echo $$files | $(am__xargs_n) 40 $(am__rm_f); }; \
}
LTLIBRARIES = $(lib_LTLIBRARIES)
libiperf_la_LIBADD =
am_libiperf_la_OBJECTS = cjson.lo iperf_api.lo iperf_error.lo \
iperf_auth.lo iperf_client_api.lo iperf_locale.lo \
iperf_server_api.lo iperf_tcp.lo iperf_udp.lo iperf_sctp.lo \
- iperf_util.lo iperf_time.lo dscp.lo net.lo tcp_info.lo \
- timer.lo units.lo
+ iperf_util.lo iperf_time.lo iperf_pthread.lo dscp.lo net.lo \
+ tcp_info.lo timer.lo units.lo
libiperf_la_OBJECTS = $(am_libiperf_la_OBJECTS)
AM_V_lt = $(am__v_lt_@AM_V@)
am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
@@ -167,9 +169,9 @@ am__iperf3_profile_SOURCES_DIST = main.c cjson.c cjson.h flowlabel.h \
iperf_auth.c iperf_client_api.c iperf_locale.c iperf_locale.h \
iperf_server_api.c iperf_tcp.c iperf_tcp.h iperf_udp.c \
iperf_udp.h iperf_sctp.c iperf_sctp.h iperf_util.c \
- iperf_util.h iperf_time.c iperf_time.h dscp.c net.c net.h \
- portable_endian.h queue.h tcp_info.c timer.c timer.h units.c \
- units.h version.h
+ iperf_util.h iperf_time.c iperf_time.h iperf_pthread.c \
+ iperf_pthread.h dscp.c net.c net.h portable_endian.h queue.h \
+ tcp_info.c timer.c timer.h units.c units.h version.h
am__objects_1 = iperf3_profile-cjson.$(OBJEXT) \
iperf3_profile-iperf_api.$(OBJEXT) \
iperf3_profile-iperf_error.$(OBJEXT) \
@@ -182,6 +184,7 @@ am__objects_1 = iperf3_profile-cjson.$(OBJEXT) \
iperf3_profile-iperf_sctp.$(OBJEXT) \
iperf3_profile-iperf_util.$(OBJEXT) \
iperf3_profile-iperf_time.$(OBJEXT) \
+ iperf3_profile-iperf_pthread.$(OBJEXT) \
iperf3_profile-dscp.$(OBJEXT) iperf3_profile-net.$(OBJEXT) \
iperf3_profile-tcp_info.$(OBJEXT) \
iperf3_profile-timer.$(OBJEXT) iperf3_profile-units.$(OBJEXT)
@@ -247,6 +250,7 @@ am__depfiles_remade = ./$(DEPDIR)/cjson.Plo ./$(DEPDIR)/dscp.Plo \
./$(DEPDIR)/iperf3_profile-iperf_client_api.Po \
./$(DEPDIR)/iperf3_profile-iperf_error.Po \
./$(DEPDIR)/iperf3_profile-iperf_locale.Po \
+ ./$(DEPDIR)/iperf3_profile-iperf_pthread.Po \
./$(DEPDIR)/iperf3_profile-iperf_sctp.Po \
./$(DEPDIR)/iperf3_profile-iperf_server_api.Po \
./$(DEPDIR)/iperf3_profile-iperf_tcp.Po \
@@ -260,14 +264,14 @@ am__depfiles_remade = ./$(DEPDIR)/cjson.Plo ./$(DEPDIR)/dscp.Plo \
./$(DEPDIR)/iperf3_profile-units.Po ./$(DEPDIR)/iperf_api.Plo \
./$(DEPDIR)/iperf_auth.Plo ./$(DEPDIR)/iperf_client_api.Plo \
./$(DEPDIR)/iperf_error.Plo ./$(DEPDIR)/iperf_locale.Plo \
- ./$(DEPDIR)/iperf_sctp.Plo ./$(DEPDIR)/iperf_server_api.Plo \
- ./$(DEPDIR)/iperf_tcp.Plo ./$(DEPDIR)/iperf_time.Plo \
- ./$(DEPDIR)/iperf_udp.Plo ./$(DEPDIR)/iperf_util.Plo \
- ./$(DEPDIR)/net.Plo ./$(DEPDIR)/t_api-t_api.Po \
- ./$(DEPDIR)/t_auth-t_auth.Po ./$(DEPDIR)/t_timer-t_timer.Po \
- ./$(DEPDIR)/t_units-t_units.Po ./$(DEPDIR)/t_uuid-t_uuid.Po \
- ./$(DEPDIR)/tcp_info.Plo ./$(DEPDIR)/timer.Plo \
- ./$(DEPDIR)/units.Plo
+ ./$(DEPDIR)/iperf_pthread.Plo ./$(DEPDIR)/iperf_sctp.Plo \
+ ./$(DEPDIR)/iperf_server_api.Plo ./$(DEPDIR)/iperf_tcp.Plo \
+ ./$(DEPDIR)/iperf_time.Plo ./$(DEPDIR)/iperf_udp.Plo \
+ ./$(DEPDIR)/iperf_util.Plo ./$(DEPDIR)/net.Plo \
+ ./$(DEPDIR)/t_api-t_api.Po ./$(DEPDIR)/t_auth-t_auth.Po \
+ ./$(DEPDIR)/t_timer-t_timer.Po ./$(DEPDIR)/t_units-t_units.Po \
+ ./$(DEPDIR)/t_uuid-t_uuid.Po ./$(DEPDIR)/tcp_info.Plo \
+ ./$(DEPDIR)/timer.Plo ./$(DEPDIR)/units.Plo
am__mv = mv -f
COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
@@ -430,12 +434,13 @@ am__sh_e_setup = case $$- in *e*) set +e;; esac
# Default flags passed to test drivers.
am__common_driver_flags = \
--color-tests "$$am__color_tests" \
+ $$am__collect_skipped_logs \
--enable-hard-errors "$$am__enable_hard_errors" \
--expect-failure "$$am__expect_failure"
# To be inserted before the command running the test. Creates the
# directory for the log if needed. Stores in $dir the directory
# containing $f, in $tst the test, in $log the log. Executes the
-# developer- defined test setup AM_TESTS_ENVIRONMENT (if any), and
+# developer-defined test setup AM_TESTS_ENVIRONMENT (if any), and
# passes TESTS_ENVIRONMENT. Set up options for the wrapper that
# will run the test scripts (or their associated LOG_COMPILER, if
# thy have one).
@@ -454,6 +459,11 @@ if test -f "./$$f"; then dir=./; \
elif test -f "$$f"; then dir=; \
else dir="$(srcdir)/"; fi; \
tst=$$dir$$f; log='$@'; \
+if test -n '$(IGNORE_SKIPPED_LOGS)'; then \
+ am__collect_skipped_logs='--collect-skipped-logs no'; \
+else \
+ am__collect_skipped_logs=''; \
+fi; \
if test -n '$(DISABLE_HARD_ERRORS)'; then \
am__enable_hard_errors=no; \
else \
@@ -517,6 +527,7 @@ AWK = @AWK@
CC = @CC@
CCDEPMODE = @CCDEPMODE@
CFLAGS = @CFLAGS@
+CPP = @CPP@
CPPFLAGS = @CPPFLAGS@
CSCOPE = @CSCOPE@
CTAGS = @CTAGS@
@@ -541,6 +552,7 @@ INSTALL_PROGRAM = @INSTALL_PROGRAM@
INSTALL_SCRIPT = @INSTALL_SCRIPT@
INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
LD = @LD@
+LDCONFIG = @LDCONFIG@
LDFLAGS = @LDFLAGS@
LIBOBJS = @LIBOBJS@
LIBS = @LIBS@
@@ -571,6 +583,10 @@ PACKAGE_URL = @PACKAGE_URL@
PACKAGE_VERSION = @PACKAGE_VERSION@
PATH_SEPARATOR = @PATH_SEPARATOR@
PKG_CONFIG = @PKG_CONFIG@
+PTHREAD_CC = @PTHREAD_CC@
+PTHREAD_CFLAGS = @PTHREAD_CFLAGS@
+PTHREAD_CXX = @PTHREAD_CXX@
+PTHREAD_LIBS = @PTHREAD_LIBS@
RANLIB = @RANLIB@
SED = @SED@
SET_MAKE = @SET_MAKE@
@@ -587,8 +603,11 @@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
am__include = @am__include@
am__leading_dot = @am__leading_dot@
am__quote = @am__quote@
+am__rm_f_notfound = @am__rm_f_notfound@
am__tar = @am__tar@
am__untar = @am__untar@
+am__xargs_n = @am__xargs_n@
+ax_pthread_config = @ax_pthread_config@
bindir = @bindir@
build = @build@
build_alias = @build_alias@
@@ -658,6 +677,8 @@ libiperf_la_SOURCES = \
iperf_util.h \
iperf_time.c \
iperf_time.h \
+ iperf_pthread.c \
+ iperf_pthread.h \
dscp.c \
net.c \
net.h \
@@ -749,12 +770,12 @@ iperf_config.h: stamp-h1
@test -f $@ || $(MAKE) $(AM_MAKEFLAGS) stamp-h1
stamp-h1: $(srcdir)/iperf_config.h.in $(top_builddir)/config.status
- @rm -f stamp-h1
- cd $(top_builddir) && $(SHELL) ./config.status src/iperf_config.h
+ $(AM_V_at)rm -f stamp-h1
+ $(AM_V_GEN)cd $(top_builddir) && $(SHELL) ./config.status src/iperf_config.h
$(srcdir)/iperf_config.h.in: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
- ($(am__cd) $(top_srcdir) && $(AUTOHEADER))
- rm -f stamp-h1
- touch $@
+ $(AM_V_GEN)($(am__cd) $(top_srcdir) && $(AUTOHEADER))
+ $(AM_V_at)rm -f stamp-h1
+ $(AM_V_at)touch $@
distclean-hdr:
-rm -f iperf_config.h stamp-h1
@@ -799,25 +820,15 @@ uninstall-binPROGRAMS:
`; \
test -n "$$list" || exit 0; \
echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \
- cd "$(DESTDIR)$(bindir)" && rm -f $$files
+ cd "$(DESTDIR)$(bindir)" && $(am__rm_f) $$files
clean-binPROGRAMS:
- @list='$(bin_PROGRAMS)'; test -n "$$list" || exit 0; \
- echo " rm -f" $$list; \
- rm -f $$list || exit $$?; \
- test -n "$(EXEEXT)" || exit 0; \
- list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
- echo " rm -f" $$list; \
- rm -f $$list
+ $(am__rm_f) $(bin_PROGRAMS)
+ test -z "$(EXEEXT)" || $(am__rm_f) $(bin_PROGRAMS:$(EXEEXT)=)
clean-noinstPROGRAMS:
- @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \
- echo " rm -f" $$list; \
- rm -f $$list || exit $$?; \
- test -n "$(EXEEXT)" || exit 0; \
- list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
- echo " rm -f" $$list; \
- rm -f $$list
+ $(am__rm_f) $(noinst_PROGRAMS)
+ test -z "$(EXEEXT)" || $(am__rm_f) $(noinst_PROGRAMS:$(EXEEXT)=)
install-libLTLIBRARIES: $(lib_LTLIBRARIES)
@$(NORMAL_INSTALL)
@@ -844,15 +855,13 @@ uninstall-libLTLIBRARIES:
done
clean-libLTLIBRARIES:
- -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
+ -$(am__rm_f) $(lib_LTLIBRARIES)
@list='$(lib_LTLIBRARIES)'; \
locs=`for p in $$list; do echo $$p; done | \
sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
sort -u`; \
- test -z "$$locs" || { \
- echo rm -f $${locs}; \
- rm -f $${locs}; \
- }
+ echo rm -f $${locs}; \
+ $(am__rm_f) $${locs}
libiperf.la: $(libiperf_la_OBJECTS) $(libiperf_la_DEPENDENCIES) $(EXTRA_libiperf_la_DEPENDENCIES)
$(AM_V_CCLD)$(LINK) -rpath $(libdir) $(libiperf_la_OBJECTS) $(libiperf_la_LIBADD) $(LIBS)
@@ -901,6 +910,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/iperf3_profile-iperf_client_api.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/iperf3_profile-iperf_error.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/iperf3_profile-iperf_locale.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/iperf3_profile-iperf_pthread.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/iperf3_profile-iperf_sctp.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/iperf3_profile-iperf_server_api.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/iperf3_profile-iperf_tcp.Po@am__quote@ # am--include-marker
@@ -917,6 +927,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/iperf_client_api.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/iperf_error.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/iperf_locale.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/iperf_pthread.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/iperf_sctp.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/iperf_server_api.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/iperf_tcp.Plo@am__quote@ # am--include-marker
@@ -935,7 +946,7 @@ distclean-compile:
$(am__depfiles_remade):
@$(MKDIR_P) $(@D)
- @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+ @: >>$@
am--depfiles: $(am__depfiles_remade)
@@ -1156,6 +1167,20 @@ iperf3_profile-iperf_time.obj: iperf_time.c
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(iperf3_profile_CFLAGS) $(CFLAGS) -c -o iperf3_profile-iperf_time.obj `if test -f 'iperf_time.c'; then $(CYGPATH_W) 'iperf_time.c'; else $(CYGPATH_W) '$(srcdir)/iperf_time.c'; fi`
+iperf3_profile-iperf_pthread.o: iperf_pthread.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(iperf3_profile_CFLAGS) $(CFLAGS) -MT iperf3_profile-iperf_pthread.o -MD -MP -MF $(DEPDIR)/iperf3_profile-iperf_pthread.Tpo -c -o iperf3_profile-iperf_pthread.o `test -f 'iperf_pthread.c' || echo '$(srcdir)/'`iperf_pthread.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/iperf3_profile-iperf_pthread.Tpo $(DEPDIR)/iperf3_profile-iperf_pthread.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='iperf_pthread.c' object='iperf3_profile-iperf_pthread.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(iperf3_profile_CFLAGS) $(CFLAGS) -c -o iperf3_profile-iperf_pthread.o `test -f 'iperf_pthread.c' || echo '$(srcdir)/'`iperf_pthread.c
+
+iperf3_profile-iperf_pthread.obj: iperf_pthread.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(iperf3_profile_CFLAGS) $(CFLAGS) -MT iperf3_profile-iperf_pthread.obj -MD -MP -MF $(DEPDIR)/iperf3_profile-iperf_pthread.Tpo -c -o iperf3_profile-iperf_pthread.obj `if test -f 'iperf_pthread.c'; then $(CYGPATH_W) 'iperf_pthread.c'; else $(CYGPATH_W) '$(srcdir)/iperf_pthread.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/iperf3_profile-iperf_pthread.Tpo $(DEPDIR)/iperf3_profile-iperf_pthread.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='iperf_pthread.c' object='iperf3_profile-iperf_pthread.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(iperf3_profile_CFLAGS) $(CFLAGS) -c -o iperf3_profile-iperf_pthread.obj `if test -f 'iperf_pthread.c'; then $(CYGPATH_W) 'iperf_pthread.c'; else $(CYGPATH_W) '$(srcdir)/iperf_pthread.c'; fi`
+
iperf3_profile-dscp.o: dscp.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(iperf3_profile_CFLAGS) $(CFLAGS) -MT iperf3_profile-dscp.o -MD -MP -MF $(DEPDIR)/iperf3_profile-dscp.Tpo -c -o iperf3_profile-dscp.o `test -f 'dscp.c' || echo '$(srcdir)/'`dscp.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/iperf3_profile-dscp.Tpo $(DEPDIR)/iperf3_profile-dscp.Po
@@ -1474,7 +1499,6 @@ distclean-tags:
am--fnord $(TEST_LOGS) $(TEST_LOGS:.log=.trs): $(am__force_recheck)
am--force-recheck:
@:
-
$(TEST_SUITE_LOG): $(TEST_LOGS)
@$(am__set_TESTS_bases); \
am__f_ok () { test -f "$$1" && test -r "$$1"; }; \
@@ -1550,10 +1574,37 @@ $(TEST_SUITE_LOG): $(TEST_LOGS)
result_count $$1 "XPASS:" $$xpass "$$red"; \
result_count $$1 "ERROR:" $$error "$$mgn"; \
}; \
+ output_system_information () \
+ { \
+ echo; \
+ { uname -a | $(AWK) '{ \
+ printf "System information (uname -a):"; \
+ for (i = 1; i < NF; ++i) \
+ { \
+ if (i != 2) \
+ printf " %s", $$i; \
+ } \
+ printf "\n"; \
+}'; } 2>&1; \
+ if test -r /etc/os-release; then \
+ echo "Distribution information (/etc/os-release):"; \
+ sed 8q /etc/os-release; \
+ elif test -r /etc/issue; then \
+ echo "Distribution information (/etc/issue):"; \
+ cat /etc/issue; \
+ fi; \
+ }; \
+ please_report () \
+ { \
+echo "Some test(s) failed. Please report this to $(PACKAGE_BUGREPORT),"; \
+echo "together with the test-suite.log file (gzipped) and your system"; \
+echo "information. Thanks."; \
+ }; \
{ \
echo "$(PACKAGE_STRING): $(subdir)/$(TEST_SUITE_LOG)" | \
$(am__rst_title); \
create_testsuite_report --no-color; \
+ output_system_information; \
echo; \
echo ".. contents:: :depth: 2"; \
echo; \
@@ -1573,26 +1624,25 @@ $(TEST_SUITE_LOG): $(TEST_LOGS)
create_testsuite_report --maybe-color; \
echo "$$col$$br$$std"; \
if $$success; then :; else \
- echo "$${col}See $(subdir)/$(TEST_SUITE_LOG)$${std}"; \
+ echo "$${col}See $(subdir)/$(TEST_SUITE_LOG) for debugging.$${std}";\
if test -n "$(PACKAGE_BUGREPORT)"; then \
- echo "$${col}Please report to $(PACKAGE_BUGREPORT)$${std}"; \
+ please_report | sed -e "s/^/$${col}/" -e s/'$$'/"$${std}"/; \
fi; \
echo "$$col$$br$$std"; \
fi; \
$$success || exit 1
check-TESTS:
- @list='$(RECHECK_LOGS)'; test -z "$$list" || rm -f $$list
- @list='$(RECHECK_LOGS:.log=.trs)'; test -z "$$list" || rm -f $$list
- @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
+ @$(am__rm_f) $(RECHECK_LOGS)
+ @$(am__rm_f) $(RECHECK_LOGS:.log=.trs)
+ @$(am__rm_f) $(TEST_SUITE_LOG)
@set +e; $(am__set_TESTS_bases); \
log_list=`for i in $$bases; do echo $$i.log; done`; \
- trs_list=`for i in $$bases; do echo $$i.trs; done`; \
- log_list=`echo $$log_list`; trs_list=`echo $$trs_list`; \
+ log_list=`echo $$log_list`; \
$(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) TEST_LOGS="$$log_list"; \
exit $$?;
recheck: all
- @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
+ @$(am__rm_f) $(TEST_SUITE_LOG)
@set +e; $(am__set_TESTS_bases); \
bases=`for i in $$bases; do echo $$i; done \
| $(am__list_recheck_tests)` || exit 1; \
@@ -1651,6 +1701,7 @@ t_auth.log: t_auth$(EXEEXT)
@am__EXEEXT_TRUE@ --log-file $$b.log --trs-file $$b.trs \
@am__EXEEXT_TRUE@ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \
@am__EXEEXT_TRUE@ "$$tst" $(AM_TESTS_FD_REDIRECT)
+
distdir: $(BUILT_SOURCES)
$(MAKE) $(AM_MAKEFLAGS) distdir-am
@@ -1715,15 +1766,15 @@ install-strip:
"INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
fi
mostlyclean-generic:
- -test -z "$(TEST_LOGS)" || rm -f $(TEST_LOGS)
- -test -z "$(TEST_LOGS:.log=.trs)" || rm -f $(TEST_LOGS:.log=.trs)
- -test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
+ -$(am__rm_f) $(TEST_LOGS)
+ -$(am__rm_f) $(TEST_LOGS:.log=.trs)
+ -$(am__rm_f) $(TEST_SUITE_LOG)
clean-generic:
distclean-generic:
- -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
- -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+ -$(am__rm_f) $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || $(am__rm_f) $(CONFIG_CLEAN_VPATH_FILES)
maintainer-clean-generic:
@echo "This command is intended for maintainers to use"
@@ -1734,7 +1785,7 @@ clean-am: clean-binPROGRAMS clean-generic clean-libLTLIBRARIES \
clean-libtool clean-noinstPROGRAMS mostlyclean-am
distclean: distclean-am
- -rm -f ./$(DEPDIR)/cjson.Plo
+ -rm -f ./$(DEPDIR)/cjson.Plo
-rm -f ./$(DEPDIR)/dscp.Plo
-rm -f ./$(DEPDIR)/iperf3-main.Po
-rm -f ./$(DEPDIR)/iperf3_profile-cjson.Po
@@ -1744,6 +1795,7 @@ distclean: distclean-am
-rm -f ./$(DEPDIR)/iperf3_profile-iperf_client_api.Po
-rm -f ./$(DEPDIR)/iperf3_profile-iperf_error.Po
-rm -f ./$(DEPDIR)/iperf3_profile-iperf_locale.Po
+ -rm -f ./$(DEPDIR)/iperf3_profile-iperf_pthread.Po
-rm -f ./$(DEPDIR)/iperf3_profile-iperf_sctp.Po
-rm -f ./$(DEPDIR)/iperf3_profile-iperf_server_api.Po
-rm -f ./$(DEPDIR)/iperf3_profile-iperf_tcp.Po
@@ -1760,6 +1812,7 @@ distclean: distclean-am
-rm -f ./$(DEPDIR)/iperf_client_api.Plo
-rm -f ./$(DEPDIR)/iperf_error.Plo
-rm -f ./$(DEPDIR)/iperf_locale.Plo
+ -rm -f ./$(DEPDIR)/iperf_pthread.Plo
-rm -f ./$(DEPDIR)/iperf_sctp.Plo
-rm -f ./$(DEPDIR)/iperf_server_api.Plo
-rm -f ./$(DEPDIR)/iperf_tcp.Plo
@@ -1798,7 +1851,8 @@ install-dvi: install-dvi-am
install-dvi-am:
install-exec-am: install-binPROGRAMS install-libLTLIBRARIES
-
+ @$(NORMAL_INSTALL)
+ $(MAKE) $(AM_MAKEFLAGS) install-exec-hook
install-html: install-html-am
install-html-am:
@@ -1820,7 +1874,7 @@ install-ps-am:
installcheck-am:
maintainer-clean: maintainer-clean-am
- -rm -f ./$(DEPDIR)/cjson.Plo
+ -rm -f ./$(DEPDIR)/cjson.Plo
-rm -f ./$(DEPDIR)/dscp.Plo
-rm -f ./$(DEPDIR)/iperf3-main.Po
-rm -f ./$(DEPDIR)/iperf3_profile-cjson.Po
@@ -1830,6 +1884,7 @@ maintainer-clean: maintainer-clean-am
-rm -f ./$(DEPDIR)/iperf3_profile-iperf_client_api.Po
-rm -f ./$(DEPDIR)/iperf3_profile-iperf_error.Po
-rm -f ./$(DEPDIR)/iperf3_profile-iperf_locale.Po
+ -rm -f ./$(DEPDIR)/iperf3_profile-iperf_pthread.Po
-rm -f ./$(DEPDIR)/iperf3_profile-iperf_sctp.Po
-rm -f ./$(DEPDIR)/iperf3_profile-iperf_server_api.Po
-rm -f ./$(DEPDIR)/iperf3_profile-iperf_tcp.Po
@@ -1846,6 +1901,7 @@ maintainer-clean: maintainer-clean-am
-rm -f ./$(DEPDIR)/iperf_client_api.Plo
-rm -f ./$(DEPDIR)/iperf_error.Plo
-rm -f ./$(DEPDIR)/iperf_locale.Plo
+ -rm -f ./$(DEPDIR)/iperf_pthread.Plo
-rm -f ./$(DEPDIR)/iperf_sctp.Plo
-rm -f ./$(DEPDIR)/iperf_server_api.Plo
-rm -f ./$(DEPDIR)/iperf_tcp.Plo
@@ -1882,7 +1938,7 @@ uninstall-am: uninstall-binPROGRAMS uninstall-includeHEADERS \
uninstall-man: uninstall-man1 uninstall-man3
-.MAKE: all check-am install-am install-strip
+.MAKE: all check-am install-am install-exec-am install-strip
.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-TESTS \
check-am clean clean-binPROGRAMS clean-generic \
@@ -1892,7 +1948,7 @@ uninstall-man: uninstall-man1 uninstall-man3
distclean-tags distdir dvi dvi-am html html-am info info-am \
install install-am install-binPROGRAMS install-data \
install-data-am install-dvi install-dvi-am install-exec \
- install-exec-am install-html install-html-am \
+ install-exec-am install-exec-hook install-html install-html-am \
install-includeHEADERS install-info install-info-am \
install-libLTLIBRARIES install-man install-man1 install-man3 \
install-pdf install-pdf-am install-ps install-ps-am \
@@ -1907,6 +1963,16 @@ uninstall-man: uninstall-man1 uninstall-man3
.PRECIOUS: Makefile
+install-exec-hook:
+ if test -n "$(LDCONFIG)"; then $(LDCONFIG) || true; fi
+
# Tell versions [3.59,3.63) of GNU make to not export all variables.
# Otherwise a system limit (for SysV at least) may be exceeded.
.NOEXPORT:
+
+# Tell GNU make to disable its built-in pattern rules.
+%:: %,v
+%:: RCS/%,v
+%:: RCS/%
+%:: s.%
+%:: SCCS/s.%
diff --git a/src/cjson.c b/src/cjson.c
index ed8c3fda6..46495f4a6 100644
--- a/src/cjson.c
+++ b/src/cjson.c
@@ -45,9 +45,8 @@
#include
#include
#include
-#ifdef HAVE_STDINT_H
#include
-#endif
+#include
#include
#ifdef ENABLE_LOCALES
@@ -90,18 +89,6 @@
#endif
#endif
-#if defined(HAVE_INTTYPES_H)
-# include
-#else
-# ifndef PRIu64
-# if sizeof(long) == 8
-# define PRIu64 "lu"
-# else
-# define PRIu64 "llu"
-# endif
-# endif
-#endif
-
typedef struct {
const unsigned char *json;
size_t position;
@@ -134,7 +121,7 @@ CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item)
{
if (!cJSON_IsNumber(item))
{
- return (double) NAN;
+ return (double) NAN; // cppcheck-suppress invalidFunctionArg
}
return item->valuedouble;
@@ -588,7 +575,7 @@ static cJSON_bool print_number(const cJSON * const item, printbuffer * const out
}
else if(d == (double)item->valueint)
{
- length = sprintf((char*)number_buffer, "%" PRIu64, item->valueint);
+ length = sprintf((char*)number_buffer, "%" PRId64, item->valueint);
}
else
{
diff --git a/src/cjson.h b/src/cjson.h
index 52da40dc3..706869762 100644
--- a/src/cjson.h
+++ b/src/cjson.h
@@ -23,9 +23,7 @@
#ifndef cJSON__h
#define cJSON__h
-#ifdef HAVE_STDINT_H
#include
-#endif
#ifdef __cplusplus
extern "C"
@@ -115,7 +113,7 @@ typedef struct cJSON
/* The type of the item, as above. */
int type;
- /* The item's string, if type==cJSON_String and type == cJSON_Raw */
+ /* The item's string, if type==cJSON_String and type == cJSON_Raw */
char *valuestring;
/* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */
int64_t valueint;
diff --git a/src/iperf.h b/src/iperf.h
index e010c2d29..b7f4575a9 100644
--- a/src/iperf.h
+++ b/src/iperf.h
@@ -1,5 +1,5 @@
/*
- * iperf, Copyright (c) 2014-2020, The Regents of the University of
+ * iperf, Copyright (c) 2014-2020, 2023, 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.
@@ -31,9 +31,8 @@
#include
#include
-#ifdef HAVE_STDINT_H
#include
-#endif
+#include
#include
#include
#ifndef _GNU_SOURCE
@@ -51,56 +50,76 @@
#include
#endif /* HAVE_CPUSET_SETAFFINITY */
-#if defined(HAVE_INTTYPES_H)
-# include
-#else
-# ifndef PRIu64
-# if sizeof(long) == 8
-# define PRIu64 "lu"
-# else
-# define PRIu64 "llu"
-# endif
-# endif
-#endif
-
#include "timer.h"
#include "queue.h"
#include "cjson.h"
#include "iperf_time.h"
+#include "portable_endian.h"
#if defined(HAVE_SSL)
#include
#include
#endif // HAVE_SSL
+#include "iperf_pthread.h"
+
+/*
+ * Atomic types highly desired, but if not, we approximate what we need
+ * with normal integers and warn.
+ */
+#ifdef HAVE_STDATOMIC_H
+#include
+#else
+#warning "No available."
+typedef uint64_t atomic_uint_fast64_t;
+#endif // HAVE_STDATOMIC_H
+
#if !defined(__IPERF_API_H)
-typedef uint64_t iperf_size_t;
+typedef uint_fast64_t iperf_size_t;
+typedef atomic_uint_fast64_t atomic_iperf_size_t;
#endif // __IPERF_API_H
+#if (defined(__vxworks)) || (defined(__VXWORKS__))
+typedef unsigned int uint
+#endif // __vxworks or __VXWORKS__
+
+struct iperf_sctp_info
+{
+ long rtt;
+ long pmtu;
+ uint32_t wnd;
+ uint32_t cwnd;
+};
+
struct iperf_interval_results
{
- iperf_size_t bytes_transferred; /* bytes transferred in this interval */
+ atomic_iperf_size_t bytes_transferred; /* bytes transferred in this interval */
struct iperf_time interval_start_time;
struct iperf_time interval_end_time;
float interval_duration;
/* for UDP */
- int interval_packet_count;
- int interval_outoforder_packets;
- int interval_cnt_error;
- int packet_count;
+ int64_t interval_packet_count;
+ int64_t interval_outoforder_packets;
+ int64_t interval_cnt_error;
+ int64_t packet_count;
double jitter;
- int outoforder_packets;
- int cnt_error;
+ int64_t outoforder_packets;
+ int64_t cnt_error;
int omitted;
#if (defined(linux) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)) && \
defined(TCP_INFO)
struct tcp_info tcpInfo; /* getsockopt(TCP_INFO) for Linux, {Free,Net,Open}BSD */
+#elif (defined(__APPLE__) && defined(__MACH__)) && defined(TCP_CONNECTION_INFO)
+ struct tcp_connection_info tcpConnInfo;
#else
/* Just placeholders, never accessed. */
char *tcpInfo;
#endif
+#if defined(HAVE_SCTP_H)
+ struct iperf_sctp_info sctp_info;
+#endif /* HAVE_SCTP_H */
long interval_retrans;
long snd_cwnd;
long snd_wnd;
@@ -109,17 +128,19 @@ struct iperf_interval_results
long rtt;
long rttvar;
long pmtu;
+ long reorder;
};
struct iperf_stream_result
{
- iperf_size_t bytes_received;
- iperf_size_t bytes_sent;
- iperf_size_t bytes_received_this_interval;
- iperf_size_t bytes_sent_this_interval;
- iperf_size_t bytes_sent_omit;
+ atomic_iperf_size_t bytes_received;
+ atomic_iperf_size_t bytes_sent;
+ atomic_iperf_size_t bytes_received_this_interval;
+ atomic_iperf_size_t bytes_sent_this_interval;
+ atomic_iperf_size_t bytes_sent_omit;
long stream_prev_total_retrans;
long stream_retrans;
+ long stream_reorder;
long stream_max_rtt;
long stream_min_rtt;
long stream_sum_rtt;
@@ -163,10 +184,21 @@ struct iperf_settings
char *client_password;
EVP_PKEY *client_rsa_pubkey;
#endif // HAVE_SSL
+ int skip_rx_copy; /* Whether to ignore received messages data, using MSG_TRUNC option */
int connect_timeout; /* socket connection timeout, in ms */
int idle_timeout; /* server idle time timeout */
unsigned int snd_timeout; /* Timeout for sending tcp messages in active mode, in us */
struct iperf_time rcv_timeout; /* Timeout for receiving messages in active mode, in us */
+ int cntl_ka; /* Use Control TCP connection Keepalive */
+ int cntl_ka_keepidle; /* Control TCP connection Keepalive idle time (TCP_KEEPIDLE) */
+ int cntl_ka_interval; /* Control TCP connection Keepalive interval between retries (TCP_KEEPINTV) */
+ int cntl_ka_count; /* Control TCP connection Keepalive number of retries (TCP_KEEPCNT) */
+ /* GSO/GRO fields always present to allow client-server negotiation regardless of local support */
+ int gso;
+ int gso_dg_size;
+ int gso_bf_size;
+ int gro;
+ int gro_bf_size;
};
struct iperf_test;
@@ -175,6 +207,10 @@ struct iperf_stream
{
struct iperf_test* test;
+ pthread_t thr;
+ int thread_created;
+ int done;
+
/* configurable members */
int local_port;
int remote_port;
@@ -199,15 +235,16 @@ struct iperf_stream
* for udp measurements - This can be a structure outside stream, and
* stream can have a pointer to this
*/
- int packet_count;
- int peer_packet_count;
- int omitted_packet_count;
+ int64_t packet_count;
+ int64_t peer_packet_count;
+ int64_t peer_omitted_packet_count;
+ int64_t omitted_packet_count;
double jitter;
double prev_transit;
- int outoforder_packets;
- int omitted_outoforder_packets;
- int cnt_error;
- int omitted_cnt_error;
+ int64_t outoforder_packets;
+ int64_t omitted_outoforder_packets;
+ int64_t cnt_error;
+ int64_t omitted_cnt_error;
uint64_t target;
struct sockaddr_storage local_addr;
@@ -266,6 +303,8 @@ enum debug_level {
struct iperf_test
{
+ pthread_mutex_t print_mutex;
+
char role; /* 'c' lient or 's' erver */
enum iperf_mode mode;
int sender_has_retransmits;
@@ -281,6 +320,7 @@ struct iperf_test
int server_port;
int omit; /* duration of omit period (-O flag) */
int duration; /* total duration of test (-t flag) */
+ int max_server_duration; /* maximum possible duration of test as enforced by the server (--max-server-duration flag) */
char *diskfile_name; /* -F option */
int affinity, server_affinity; /* -A option */
#if defined(HAVE_CPUSET_SETAFFINITY)
@@ -307,6 +347,7 @@ struct iperf_test
char *server_authorized_users;
EVP_PKEY *server_rsa_private_key;
int server_skew_threshold;
+ int use_pkcs1_padding;
#endif // HAVE_SSL
/* boolean variables for Options */
@@ -317,6 +358,10 @@ struct iperf_test
int bidirectional; /* --bidirectional */
int verbose; /* -V option - verbose mode */
int json_output; /* -J option - JSON output */
+ int json_stream; /* --json-stream */
+ int json_stream_full_output; /* --json-stream-full-output */
+ void (*json_callback) (struct iperf_test *, char *); /* allow user apps to receive the
+ JSON strings,instead of writing them to the output file */
int zerocopy; /* -Z option - use sendfile */
int debug; /* -d option - enable debug */
enum debug_level debug_level; /* -d option option - level of debug messages to show */
@@ -327,6 +372,7 @@ struct iperf_test
int repeating_payload; /* --repeating-payload */
int timestamps; /* --timestamps */
char *timestamp_format;
+ int mptcp; /* -m, --mptcp */
char *json_output_string; /* rendered JSON output if json_output is set */
/* Select related parameters */
@@ -351,11 +397,11 @@ struct iperf_test
int num_streams; /* total streams in the test (-P) */
- iperf_size_t bytes_sent;
- iperf_size_t blocks_sent;
+ atomic_iperf_size_t bytes_sent;
+ atomic_iperf_size_t blocks_sent;
- iperf_size_t bytes_received;
- iperf_size_t blocks_received;
+ atomic_iperf_size_t bytes_received;
+ atomic_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 */
@@ -411,6 +457,8 @@ struct iperf_test
#define UDP_BUFFER_EXTRA 1024
+#define MAX_PARAMS_JSON_STRING 8 * 1024
+
/* constants for command line arg sanity checks */
#define MB (1024 * 1024)
#define MAX_TCP_BUFFER (512 * MB)
@@ -422,8 +470,9 @@ struct iperf_test
#define MIN_INTERVAL 0.1
#define MAX_INTERVAL 60.0
#define MAX_TIME 86400
+#define MAX_OMIT_TIME 600
#define MAX_BURST 1000
-#define MAX_MSS (9 * 1024)
+#define MAX_MSS (32 * 1024 - 1)
#define MAX_STREAMS 128
#define TIMESTAMP_FORMAT "%c "
@@ -431,11 +480,22 @@ struct iperf_test
extern int gerror; /* error value from getaddrinfo(3), for use in internal error handling */
/* UDP "connect" message and reply (textual value for Wireshark, etc. readability - legacy was numeric) */
+
+#if BYTE_ORDER == BIG_ENDIAN
+#define UDP_CONNECT_MSG 0x39383736
+#define UDP_CONNECT_REPLY 0x36373839
+#define LEGACY_UDP_CONNECT_REPLY 0xb168de3a
+#else
#define UDP_CONNECT_MSG 0x36373839 // "6789" - legacy value was 123456789
#define UDP_CONNECT_REPLY 0x39383736 // "9876" - legacy value was 987654321
#define LEGACY_UDP_CONNECT_REPLY 987654321 // Old servers may still reply with the legacy value
+#endif
/* In Reverse mode, maximum number of packets to wait for "accept" response - to handle out of order packets */
#define MAX_REVERSE_OUT_OF_ORDER_PACKETS 2
+#define GSO_MAX_DG_IN_BF (1 << 7UL) // 128 - the Linux Kernel limit hardcoded as `UDP_MAX_SEGMENTS (1 << 7UL)` in `udpgso.c`
+#define GSO_BF_MAX_SIZE MAX_UDP_BLOCKSIZE
+#define GRO_BF_MAX_SIZE MAX_UDP_BLOCKSIZE
+
#endif /* !__IPERF_H */
diff --git a/src/iperf3.1 b/src/iperf3.1
index b76bd8795..626c6372f 100644
--- a/src/iperf3.1
+++ b/src/iperf3.1
@@ -1,4 +1,4 @@
-.TH IPERF3 1 "September 2022" ESnet "User Manuals"
+.TH IPERF3 1 "April 2026" ESnet "User Manuals"
.SH NAME
iperf3 \- perform network throughput tests
.SH SYNOPSIS
@@ -43,7 +43,7 @@ iperf3 clients (in other words, the iperf3 program run in client
mode).
The client mode can be started using the -c command-line option,
which also requires a host to which iperf3 should connect.
-The host can by specified by hostname, IPv4 literal, or IPv6 literal:
+The host can be specified by hostname, IPv4 literal, or IPv6 literal:
.IP
\fCiperf3 -c iperf3.example.com\fR
.IP
@@ -96,9 +96,11 @@ test by specifying the --get-server-output flag.
Either the client or the server can produce its output in a JSON
structure, useful for integration with other programs, by passing it
the -J flag.
-Because the contents of the JSON structure are only completely known
+Normally the contents of the JSON structure are only completely known
after the test has finished, no JSON output will be emitted until the
end of the test.
+By enabling line-delimited JSON multiple objects will be emitted to
+provide a real-time parsable JSON output.
.PP
iperf3 has a (overly) large set of command-line options that can be
used to set the parameters of a test.
@@ -108,17 +110,18 @@ viewed by running iperf3 with the -h flag.
.SH "GENERAL OPTIONS"
.TP
.BR -p ", " --port " \fIn\fR"
-set server port to listen on/connect to to \fIn\fR (default 5201)
+Set server port to listen on/connect to to \fIn\fR (default 5201)
.TP
-.BR -f ", " --format " "
-[kmgtKMGT] format to report: Kbits/Mbits/Gbits/Tbits
+.BR -f ", " --format " [kmgtKMGT]"
+Set format to report: Kbits/Mbits/Gbits/Tbits
.TP
.BR -i ", " --interval " \fIn\fR"
-pause \fIn\fR seconds between periodic throughput reports;
-default is 1, use 0 to disable
+Pause \fIn\fR seconds between periodic throughput reports;
+default is 1, use 0 to disable.
.TP
.BR -I ", " --pidfile " \fIfile\fR"
-write a file with the process ID, most useful when running as a daemon.
+Write a file with the process ID.
+This option is most useful when running as a daemon.
.TP
.BR -F ", " --file " \fIname\fR"
Use a file as the source (on the sender) or sink (on the receiver) of
@@ -127,7 +130,10 @@ This feature is used for finding whether or not the storage subsystem
is the bottleneck for file transfers.
It does not turn iperf3 into a file transfer tool.
The length, attributes, and in some cases contents of the received
-file may not match those of the original file.
+file may not match those of the original file. This option is
+unavailable when doing
+.B --udp
+tests.
.TP
.BR -A ", " --affinity " \fIn/n,m\fR"
Set the CPU affinity, if possible (Linux, FreeBSD, and Windows only).
@@ -141,31 +147,46 @@ to a single CPU (as opposed to a set containing potentially multiple
CPUs).
.TP
.BR -B ", " --bind " \fIhost\fR[\fB%\fIdev\fR]"
-bind to the specific interface associated with address \fIhost\fR.
+Bind to the specific interface associated with address \fIhost\fR.
If an optional interface is specified, it is treated as a shortcut
for \fB--bind-dev \fIdev\fR.
-Note that a percent sign and interface device name are required for IPv6 link-local address literals.
+Note that a percent sign and interface device name are required for
+IPv6 link-local address literals, in order to set the link-local
+scope.
.TP
.BR --bind-dev " \fIdev\fR"
-bind to the specified network interface.
-This option uses SO_BINDTODEVICE, and may require root permissions.
+Bind to the specified network interface.
+This option uses SO_BINDTODEVICE or IP_BOUND_IF, and may require root permissions.
(Available on Linux and possibly other systems.)
.TP
.BR -V ", " --verbose " "
-give more detailed output
+Produce more detailed output.
.TP
.BR -J ", " --json " "
-output in JSON format
+Output in JSON format instead of the default human-readable
+output.
+.TP
+.BR --json-stream " "
+Output in line-delimited JSON format instead of the default
+human-readable output. This option overrides the
+.B --json
+option, if that option was also specified.
+.TP
+.BR --json-stream-full-output " "
+Output in JSON format with JSON streams enabled. This flag only takes
+effect if the
+.B --json-stream
+option was also specified.
.TP
.BR --logfile " \fIfile\fR"
-send output to a log file.
+Send output to a log file.
.TP
.BR --forceflush " "
-force flushing output at every interval.
+Force flushing output at every interval.
Used to avoid buffering when sending output to pipe.
.TP
.BR --timestamps "[\fB=\fIformat\fR]"
-prepend a timestamp at the start of each output line.
+Prepend a timestamp at the start of each output line.
By default, timestamps have the format emitted by
.BR ctime ( 1 ).
Optionally, \fC=\fR followed by
@@ -176,87 +197,113 @@ If this optional format is given, the \fC=\fR must immediately
follow the \fB--timestamps\fR option with no whitespace intervening.
.TP
.BR --rcv-timeout " \fI#\fR"
-set idle timeout for receiving data during active tests. The receiver
+Set idle timeout for receiving data during active tests. The receiver
will halt a test if no data is received from the sender for this
-number of ms (default to 12000 ms, or 2 minutes).
+number of ms (default to 120000 ms, or 2 minutes).
.TP
.BR --snd-timeout " \fI#\fR"
-set timeout for unacknowledged TCP data (on both test and control
+Set timeout for unacknowledged TCP data (on both test and control
connections) This option can be used to force a faster test timeout
in case of a network partition during a test. The required
parameter is specified in ms, and defaults to the system settings.
This functionality depends on the TCP_USER_TIMEOUT socket option, and
will not work on systems that do not support it.
.TP
+.BR --use-pkcs1-padding
+This option is only meaningful when using iperf3's authentication
+features. Versions of iperf3 prior to 3.17 used PCKS1 padding in the
+RSA-encrypted credentials, which was vulnerable to a side-channel
+attack that could reveal a server's private key. Beginning with
+iperf-3.17, OAEP padding is used, however this is a breaking change
+that is not compatible with older iperf3 versions. Use this option to
+preserve the less secure, but more compatible, behavior.
+.TP
+.BR -m ", " --mptcp " "
+Use the MPTCP variant for the current protocol. This only applies to
+TCP and enables MPTCP usage.
+.TP
.BR -d ", " --debug " "
-emit debugging output.
+Emit debugging output.
Primarily (perhaps exclusively) of use to developers.
.TP
.BR -v ", " --version " "
-show version information and quit
+Show version information and quit.
.TP
.BR -h ", " --help " "
-show a help synopsis
+Show a help synopsis.
.SH "SERVER SPECIFIC OPTIONS"
.TP
.BR -s ", " --server " "
-run in server mode
+Run in server mode.
.TP
.BR -D ", " --daemon " "
-run the server in background as a daemon
+Run the server in background as a daemon.
.TP
.BR -1 ", " --one-off
-handle one client connection, then exit. If an idle time is set, the
-server will exit after that amount of time with no connection.
+Handle (at most) one client connection, then exit.
+If an idle time is set, the server will exit after that amount of time
+with no connection.
.TP
.BR --idle-timeout " \fIn\fR"
-restart the server after \fIn\fR seconds in case it gets stuck. In
-one-off mode, this is the number of seconds the server will wait
+Restart the server after \fIn\fR seconds in case it gets stuck.
+In one-off mode, this is the number of seconds the server will wait
before exiting.
.TP
-.BR --server-bitrate-limit " \fIn\fR[KMGT]"
-set a limit on the server side, which will cause a test to abort if
+.BR --server-max-duration " \fIn\fR"
+The maximum time, in seconds, that an iperf client can run against the server.
+When the sum of the client's time and omit values exceeds the max duration set by the server
+or the client's time value is 0, the measurement is rejected.
+.TP
+.BR --server-bitrate-limit " \fIn\fR[KMGT][/\fCn\fR]"
+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.
+is 0, 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
+.B
+/
+character 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
+Path to the RSA private key (not password-protected) used to decrypt
authentication credentials from the client (if built with OpenSSL
support).
.TP
.BR --authorized-users-path " \fIfile\fR"
-path to the configuration file containing authorized users credentials to run
-iperf tests (if built with OpenSSL support).
-The file is a comma separated list of usernames and password hashes;
-more information on the structure of the file can be found in the
-EXAMPLES section.
-.TP
-.BR --time-skew-threshold second " \fIseconds\fR"
-time skew threshold (in seconds) between the server and client
-during the authentication process.
+Path to the configuration file containing authorized users credentials
+to run iperf tests (if built with OpenSSL support). The file is a
+comma separated list of usernames and password hashes; more
+information on the structure of the file can be found in the EXAMPLES
+section.
+.TP
+.BR --time-skew-threshold " \fIseconds\fR"
+Specify the allowable time skew threshold (in seconds) between the
+server and client during the authentication process.
.SH "CLIENT SPECIFIC OPTIONS"
.TP
.BR -c ", " --client " \fIhost\fR[\fB%\fIdev\fR]"
-run in client mode, connecting to the specified server.
+Run in client mode, connecting to the specified server.
By default, a test consists of sending data from the client to the
server, unless the \-R flag is specified.
If an optional interface is specified, it is treated as a shortcut
for \fB--bind-dev \fIdev\fR.
-Note that a percent sign and interface device name are required for IPv6 link-local address literals.
+Note that a percent sign and interface device name are required for
+IPv6 link-local address literals.
.TP
.BR --sctp
-use SCTP rather than TCP (FreeBSD and Linux)
+Use SCTP for tests rather than TCP (FreeBSD and Linux).
+Note that TCP communication is still used for the control connection
+between client and server.
.TP
.BR -u ", " --udp
-use UDP rather than TCP
+Use UDP for tests rather than TCP.
+Note that TCP communication is still used for the control connection
+between client and server.
.TP
.BR --connect-timeout " \fIn\fR"
-set timeout for establishing the initial control connection to the
+Set timeout for establishing the initial control connection to the
server, in milliseconds.
The default behavior is the operating system's timeout for TCP
connection establishment.
@@ -264,24 +311,29 @@ Providing a shorter value may speed up detection of a down iperf3
server.
.TP
.BR -b ", " --bitrate " \fIn\fR[KMGT]"
-set target bitrate to \fIn\fR bits/sec (default 1 Mbit/sec for UDP,
+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
separately to each stream.
You can also add a '/' and a number to the bitrate specifier.
This is called "burst mode".
-It will send the given number of packets without pausing, even if that
+It will perform the given number of sends without pausing,
+even if that
temporarily exceeds the specified throughput limit.
Setting the target bitrate to 0 will disable bitrate limits
(particularly useful for UDP tests).
This throughput limit is implemented internally inside iperf3, and is
available on all platforms.
-Compare with the \--fq-rate flag.
-This option replaces the \--bandwidth flag, which is now deprecated
+Compare with the
+.B --fq-rate
+flag.
+This option replaces the
+.B --bandwidth
+flag, which is now deprecated
but (at least for now) still accepted.
.TP
.BR --pacing-timer " \fIn\fR[KMGT]"
-set pacing timer interval in microseconds (default 1000 microseconds,
+Set pacing timer interval in microseconds (default 1000 microseconds,
or 1 ms).
This controls iperf3's internal pacing timer for the \-b/\--bitrate
option.
@@ -302,72 +354,99 @@ The default is no fair-queueing based pacing.
.TP
.BR --no-fq-socket-pacing
This option is deprecated and will be removed.
-It is equivalent to specifying --fq-rate=0.
+It is equivalent to specifying
+.BR
+--fq-rate=0 .
.TP
.BR -t ", " --time " \fIn\fR"
-time in seconds to transmit for (default 10 secs)
+Set the test duration in seconds (default 10 secs).
+The
+.BR
+-t , -n ", and" -k
+options are mutually exclusive.
.TP
.BR -n ", " --bytes " \fIn\fR[KMGT]"
-number of bytes to transmit (instead of \-t)
+Set the number of bytes to transmit.
+The
+.BR
+-t , -n ", and" -k
+options are mutually exclusive.
.TP
.BR -k ", " --blockcount " \fIn\fR[KMGT]"
-number of blocks (packets) to transmit (instead of \-t or \-n)
+Set the number of blocks (packets) to transmit.
+The
+.BR
+-t , -n ", and" -k
+options are mutually exclusive.
.TP
.BR -l ", " --length " \fIn\fR[KMGT]"
-length of buffer to read or write. For TCP tests, the default value
-is 128KB.
+Set the length of the 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
sending size based on the path MTU; if that cannot be determined it
uses 1460 bytes as a sending size.
For SCTP tests, the default size is 64KB.
.TP
.BR --cport " \fIport\fR"
-bind data streams to a specific client port (for TCP and UDP only,
-default is to use an ephemeral port)
+Bind data streams to a specific TCP or UDP client port (for TCP
+and UDP only, default is to use an ephemeral port).
.TP
.BR -P ", " --parallel " \fIn\fR"
-number of parallel client streams to run. Note that iperf3 is single threaded, so if you are CPU bound, this will not yield higher throughput.
+Set the number of parallel client streams to run. Beginning with
+iperf-3.16, iperf3 will spawn off a separate thread for each test
+stream.
+Using multiple streams may result in higher throughput than a
+single stream, in cases where network throughput is CPU-limited.
.TP
.BR -R ", " --reverse
-reverse the direction of a test, so that the server sends data to the
-client
+Reverse the direction of a test, so that the server sends data to the
+client.
.TP
.BR --bidir
-test in both directions (normal and reverse), with both the client and
+Test in both directions (normal and reverse), with both the client and
server sending and receiving data simultaneously
.TP
.BR -w ", " --window " \fIn\fR[KMGT]"
-set socket buffer size / window size.
-This value gets sent to the server and used on that side too; on both
-sides this option sets both the sending and receiving socket buffer sizes.
-This option can be used to set (indirectly) the maximum TCP window size.
-Note that on Linux systems, the effective maximum window size is approximately
-double what is specified by this option (this behavior is not a bug in iperf3
-but a "feature" of the Linux kernel, as documented by tcp(7) and socket(7)).
+Set t he socket buffer size / window size.
+This value gets sent to the server and used on that side too;
+on both sides this option sets both the sending and receiving
+socket buffer sizes.
+This option can be used to set (indirectly) the maximum TCP window
+size.
+Note that on Linux systems, the effective maximum window size is
+approximately
+double what is specified by this option.
+This behavior is not a bug in iperf3 but a feature of the
+Linux kernel, as documented by
+.BR tcp ( 7 )
+and
+.BR socket ( 7 )).
.TP
.BR -M ", " --set-mss " \fIn\fR"
-set TCP/SCTP maximum segment size (MTU - 40 bytes)
+Set the TCP/SCTP maximum segment size (MTU - 40 bytes).
.TP
.BR -N ", " --no-delay " "
-set TCP/SCTP no delay, disabling Nagle's Algorithm
+Set the TCP/SCTP no delay option, disabling Nagle's Algorithm.
.TP
.BR -4 ", " --version4 " "
-only use IPv4
+Force the use of IPv4.
.TP
.BR -6 ", " --version6 " "
-only use IPv6
+Force the use of IPv6.
.TP
.BR -S ", " --tos " \fIn\fR"
-set the IP type of service. The usual prefixes for octal and hex can be used,
+Set the IP type of service bits.
+The usual prefixes for octal and hex can be used,
i.e. 52, 064 and 0x34 all specify the same value.
.TP
.BR "--dscp " \fIdscp\fR
-set the IP DSCP bits. Both numeric and symbolic values are accepted. Numeric
-values can be specified in decimal, octal and hex (see --tos above). To set
-both the DSCP bits and the ECN bits, use --tos.
+Set the IP DSCP bits. Both numeric and symbolic values are accepted. Numeric
+values can be specified in decimal, octal and hex (see
+.B --tos
+above).
.TP
.BR -L ", " --flowlabel " \fIn\fR"
-set the IPv6 flow label (currently only supported on Linux)
+Set the IPv6 flow label (currently only supported on Linux).
.TP
.BR -X ", " --xbind " \fIname\fR"
Bind SCTP associations to a specific subset of links using sctp_bindx(3).
@@ -380,7 +459,11 @@ association, and is supported for both iperf servers and clients
(the latter are supported by passing the first \fB--X\fR argument to bind(2)).
Hostnames are accepted as arguments and are resolved using
getaddrinfo(3).
-If the \fB--4\fR or \fB--6\fR flags are specified, names
+If the
+.B --4
+or
+.B --6
+flags are also specified, names
which do not resolve to addresses within the
specified protocol family will be ignored.
.TP
@@ -391,12 +474,19 @@ Set number of SCTP streams.
Use a "zero copy" method of sending data, such as sendfile(2),
instead of the usual write(2).
.TP
+.BR --skip-rx-copy
+Ignored received packet data, using the MSG_TRUNC flag to the
+recv(2) system call.
+.TP
.BR -O ", " --omit " \fIn\fR"
-Perform pre-test for N seconds and omit the pre-test statistics, to skip past the TCP slow-start
+Perform pre-test for
+.I n
+seconds and omit the pre-test statistics, to skip past the TCP slow-start
period.
.TP
.BR -T ", " --title " \fIstr\fR"
-Prefix every output line with this string.
+Prefix every output line with the string
+.IR str .
.TP
.BR --extra-data " \fIstr\fR"
Specify an extra data string field to be included in JSON output.
@@ -407,14 +497,15 @@ older
.B --linux-congestion
synonym for this flag is accepted but is deprecated.
.TP
-.BR "--get-server-output"
+.BR --get-server-output
Get the output from the server.
The output format is determined by the server (in particular, if the
server was invoked with the \fB--json\fR flag, the output will be in
JSON format, otherwise it will be in human-readable format).
If the client is run with \fB--json\fR, the server output is included
in a JSON object; otherwise it is appended at the bottom of the
-human-readable output.
+human-readable output. Note that the server output is available only
+if the test completes, not if it is interrupted.
.TP
.BR --udp-counters-64bit
Use 64-bit counters in UDP test packets.
@@ -423,6 +514,17 @@ or high-bitrate UDP tests. Both client and server need to be running
at least version 3.1 for this option to work. It may become the
default behavior at some point in the future.
.TP
+.BR --gsro
+Enable UDP Generic Segmentation Offload (GSO) on the sender and
+Generic Receive Offload (GRO) on the receiver, where supported by
+the operating system and network hardware (currently Linux only).
+GSO allows the network stack to aggregate multiple UDP datagrams
+into larger packets, improving throughput and reducing CPU overhead.
+This is a client-only option; the client communicates the GSO/GRO
+settings to the server automatically.
+This feature is disabled by default and must be explicitly enabled
+with this flag.
+.TP
.BR --repeating-payload
Use repeating pattern in payload, instead of random bytes.
The same payload is used in iperf2 (ASCII '0..9' repeating).
@@ -435,15 +537,17 @@ Set the IPv4 Don't Fragment (DF) bit on outgoing packets.
Only applicable to tests doing UDP over IPv4.
.TP
.BR --username " \fIusername\fR"
-username to use for authentication to the iperf server (if built with
-OpenSSL support).
-The password will be prompted for interactively when the test is run. Note,
-the password to use can also be specified via the IPERF3_PASSWORD environment
-variable. If this variable is present, the password prompt will be skipped.
+Specify username to use for authentication to the iperf server
+(if built with OpenSSL support).
+The password will be prompted for interactively when the test is run.
+Note the password can also be specified via the IPERF3_PASSWORD
+environment variable. If this variable is present, the password
+prompt will be skipped.
.TP
.BR --rsa-public-key-path " \fIfile\fR"
-path to the RSA public key used to encrypt authentication credentials
-(if built with OpenSSL support)
+Set path to the RSA public key used to encrypt authentication
+credentials
+(if built with OpenSSL support).
.SH EXAMPLES
.SS "Authentication - RSA Keypair"
@@ -455,15 +559,15 @@ password set.
The public key must be in PEM format and use SubjectPrefixKeyInfo encoding.
An example of a set of UNIX/Linux commands using OpenSSL
to generate a correctly-formed keypair follows:
-.sp 1
-.in +.5i
-> openssl genrsa -des3 -out private.pem 2048
+.IP
+\fC> openssl genrsa -des3 -out private.pem 2048\fR
.sp 0
-> openssl rsa -in private.pem -outform PEM -pubout -out public.pem
+\fC> openssl rsa -in private.pem -outform PEM -pubout -out public.pem\fR
.sp 0
-> openssl rsa -in private.pem -out private_not_protected.pem -outform PEM
-.in -.5i
-.sp 1
+\fC> openssl rsa -in private.pem -out private_not_protected.pem \\ \fR
+.sp 0
+\fC -outform PEM\fR
+.PP
After these commands, the public key will be contained in the file
public.pem and the private key will be contained in the file
private_not_protected.pem.
@@ -477,25 +581,21 @@ The file can also contain commented lines (starting with the \fC#\fR
character).
An example of commands to generate the password hash on a UNIX/Linux system
is given below:
-.sp 1
-.in +.5i
-> S_USER=mario S_PASSWD=rossi
+.IP
+\fC> S_USER=mario S_PASSWD=rossi\fR
.sp 0
-> echo -n "{$S_USER}$S_PASSWD" | sha256sum | awk '{ print $1 }'
-.in -.5i
-.sp 1
+\fC> echo -n "{$S_USER}$S_PASSWD" | sha256sum | awk '{ print $1 }'\fR
+.PP
An example of a password file (with an entry corresponding to the
above username and password) is given below:
+.IP
+\fC> cat credentials.csv\fR
+.in -.5i
.sp 0
-.in +.5i
-> cat credentials.csv
-.sp 0
-# file format: username,sha256
+\fC# file format: username,sha256\fR
.sp 0
-mario,bf7a49a846d44b454a5d11e7acfaf13d138bbe0b7483aa3e050879700572709b
-.in -.5i
-.sp 1
-
+\fCmario,bf7a49a846d44b454a5d11e7acfaf13d138bbe0b7483aa3e050879700572709b\fR
+.in +.5i
.SH AUTHORS
A list of the contributors to iperf3 can be found within the
documentation located at
diff --git a/src/iperf_api.c b/src/iperf_api.c
index a441734d0..fb4273f1e 100644
--- a/src/iperf_api.c
+++ b/src/iperf_api.c
@@ -1,5 +1,5 @@
/*
- * iperf, Copyright (c) 2014-2022, The Regents of the University of
+ * iperf, Copyright (c) 2014-2026, The Regents of the University of
* California, through Lawrence Berkeley National Laboratory (subject
* to receipt of any required approvals from the U.S. Dept. of
* Energy). All rights reserved.
@@ -46,16 +46,13 @@
#include
#include
#include
-#ifdef HAVE_STDINT_H
#include
-#endif
#include
#include
#include
#include
#include
#include
-#include
#include
#if defined(HAVE_CPUSET_SETAFFINITY)
@@ -63,6 +60,10 @@
#include
#endif /* HAVE_CPUSET_SETAFFINITY */
+#if defined(HAVE_IP_BOUND_IF)
+#include
+#endif
+
#if defined(__CYGWIN__) || defined(_WIN32) || defined(_WIN64) || defined(__WINDOWS__)
#define CPU_SETSIZE __CPU_SETSIZE
#endif /* __CYGWIN__, _WIN32, _WIN64, __WINDOWS__ */
@@ -74,8 +75,9 @@
#include "net.h"
#include "iperf.h"
#include "iperf_api.h"
-#include "iperf_udp.h"
#include "iperf_tcp.h"
+#include "iperf_time.h"
+#include "iperf_udp.h"
#if defined(HAVE_SCTP_H)
#include "iperf_sctp.h"
#endif /* HAVE_SCTP_H */
@@ -101,7 +103,8 @@ static int diskfile_send(struct iperf_stream *sp);
static int diskfile_recv(struct iperf_stream *sp);
static int JSON_write(int fd, cJSON *json);
static void print_interval_results(struct iperf_test *test, struct iperf_stream *sp, cJSON *json_interval_streams);
-static cJSON *JSON_read(int fd);
+static cJSON *JSON_read(int fd, int max_size);
+static int JSONStream_Output(struct iperf_test *test, const char* event_name, cJSON* obj);
/*************************** Print usage functions ****************************/
@@ -326,6 +329,12 @@ iperf_get_test_json_output_string(struct iperf_test *ipt)
return ipt->json_output_string;
}
+int
+iperf_get_test_json_stream(struct iperf_test *ipt)
+{
+ return ipt->json_stream;
+}
+
int
iperf_get_test_zerocopy(struct iperf_test *ipt)
{
@@ -476,6 +485,10 @@ iperf_set_test_stats_interval(struct iperf_test *ipt, double stats_interval)
void
iperf_set_test_state(struct iperf_test *ipt, signed char state)
{
+ if (ipt->debug_level >= DEBUG_LEVEL_INFO) {
+ iperf_printf(ipt, "State change: State set to %d-%s (from %d-%s)\n",
+ state, state_to_text(state), ipt->state, state_to_text(ipt->state));
+ }
ipt->state = state;
}
@@ -593,6 +606,30 @@ iperf_set_mapped_v4(struct iperf_test *ipt, const int val)
ipt->mapped_v4 = val;
}
+void
+iperf_set_on_new_stream_callback(struct iperf_test* ipt, void (*callback)(struct iperf_stream *))
+{
+ ipt->on_new_stream = callback;
+}
+
+void
+iperf_set_on_test_start_callback(struct iperf_test* ipt, void (*callback)(struct iperf_test *))
+{
+ ipt->on_test_start = callback;
+}
+
+void
+iperf_set_on_test_connect_callback(struct iperf_test* ipt, void (*callback)(struct iperf_test *))
+{
+ ipt->on_connect = callback;
+}
+
+void
+iperf_set_on_test_finish_callback(struct iperf_test* ipt, void (*callback)(struct iperf_test *))
+{
+ ipt->on_test_finish = callback;
+}
+
static void
check_sender_has_retransmits(struct iperf_test *ipt)
{
@@ -658,6 +695,24 @@ iperf_set_test_json_output(struct iperf_test *ipt, int json_output)
ipt->json_output = json_output;
}
+void
+iperf_set_test_json_stream(struct iperf_test *ipt, int json_stream)
+{
+ ipt->json_stream = json_stream;
+}
+
+void
+iperf_set_test_json_stream_full_output( struct iperf_test* ipt, int json_stream_full_output )
+{
+ ipt->json_stream_full_output = json_stream_full_output;
+}
+
+void
+iperf_set_test_json_callback(struct iperf_test *ipt, void (*callback)(struct iperf_test *, char *))
+{
+ ipt->json_callback = callback;
+}
+
int
iperf_has_zerocopy( void )
{
@@ -701,6 +756,12 @@ iperf_set_test_client_rsa_pubkey(struct iperf_test *ipt, const char *client_rsa_
ipt->settings->client_rsa_pubkey = load_pubkey_from_base64(client_rsa_pubkey_base64);
}
+void
+iperf_set_test_client_rsa_pubkey_from_file(struct iperf_test *ipt, const char *client_rsa_pubkey_file)
+{
+ ipt->settings->client_rsa_pubkey = load_pubkey_from_file(client_rsa_pubkey_file);
+}
+
void
iperf_set_test_server_authorized_users(struct iperf_test *ipt, const char *server_authorized_users)
{
@@ -718,6 +779,12 @@ iperf_set_test_server_rsa_privkey(struct iperf_test *ipt, const char *server_rsa
{
ipt->server_rsa_private_key = load_privkey_from_base64(server_rsa_privkey_base64);
}
+
+void
+iperf_set_test_server_rsa_privkey_from_file(struct iperf_test *ipt, const char *server_rsa_privkey_file)
+{
+ ipt->server_rsa_private_key = load_privkey_from_file(server_rsa_privkey_file);
+}
#endif // HAVE_SSL
void
@@ -857,7 +924,7 @@ void
iperf_on_test_start(struct iperf_test *test)
{
if (test->json_output) {
- cJSON_AddItemToObject(test->json_start, "test_start", iperf_json_printf("protocol: %s num_streams: %d blksize: %d omit: %d duration: %d bytes: %d blocks: %d reverse: %d tos: %d target_bitrate: %d bidir: %d fqrate: %d", test->protocol->name, (int64_t) test->num_streams, (int64_t) test->settings->blksize, (int64_t) test->omit, (int64_t) test->duration, (int64_t) test->settings->bytes, (int64_t) test->settings->blocks, test->reverse?(int64_t)1:(int64_t)0, (int64_t) test->settings->tos, (int64_t) test->settings->rate, (int64_t) test->bidirectional, (uint64_t) test->settings->fqrate));
+ cJSON_AddItemToObject(test->json_start, "test_start", iperf_json_printf("protocol: %s num_streams: %d blksize: %d omit: %d duration: %d bytes: %d blocks: %d reverse: %d tos: %d target_bitrate: %d bidir: %d fqrate: %d interval: %f gso: %d gro: %d", test->protocol->name, (int64_t) test->num_streams, (int64_t) test->settings->blksize, (int64_t) test->omit, (int64_t) test->duration, (int64_t) test->settings->bytes, (int64_t) test->settings->blocks, test->reverse?(int64_t)1:(int64_t)0, (int64_t) test->settings->tos, (int64_t) test->settings->rate, (int64_t) test->bidirectional, (uint64_t) test->settings->fqrate, test->stats_interval, (uint64_t) test->settings->gso, (uint64_t) test->settings->gro));
} else {
if (test->verbose) {
if (test->settings->bytes)
@@ -868,8 +935,12 @@ iperf_on_test_start(struct iperf_test *test)
iperf_printf(test, test_start_time, test->protocol->name, test->num_streams, test->settings->blksize, test->omit, test->duration, test->settings->tos);
}
}
+ if (test->json_stream) {
+ JSONStream_Output(test, "start", test->json_start);
+ }
}
+
/* This converts an IPv6 string address from IPv4-mapped format into regular
** old IPv4 format, which is easier on the eyes of network veterans.
**
@@ -895,7 +966,6 @@ mapped_v4_to_regular_v4(char *str)
void
iperf_on_connect(struct iperf_test *test)
{
- time_t now_secs;
const char* rfc1123_fmt = "%a, %d %b %Y %H:%M:%S %Z";
char now_str[100];
char ipr[INET6_ADDRSTRLEN];
@@ -904,11 +974,17 @@ iperf_on_connect(struct iperf_test *test)
struct sockaddr_in *sa_inP;
struct sockaddr_in6 *sa_in6P;
socklen_t len;
+ struct iperf_time now;
+ time_t now_secs;
+ unsigned long long now_millisecs;
+
+ iperf_time_now_wallclock(&now);
+ now_millisecs = (time_t) iperf_time_in_usecs(&now) / 1000;
+ now_secs = (time_t) (now_millisecs / 1000);
- now_secs = time((time_t*) 0);
(void) strftime(now_str, sizeof(now_str), rfc1123_fmt, gmtime(&now_secs));
if (test->json_output)
- cJSON_AddItemToObject(test->json_start, "timestamp", iperf_json_printf("time: %s timesecs: %d", now_str, (int64_t) now_secs));
+ cJSON_AddItemToObject(test->json_start, "timestamp", iperf_json_printf("time: %s timesecs: %d timemillisecs: %d", now_str, (int64_t) now_secs, now_millisecs));
else if (test->verbose)
iperf_printf(test, report_time, now_str);
@@ -1034,13 +1110,16 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv)
{"one-off", no_argument, NULL, '1'},
{"verbose", no_argument, NULL, 'V'},
{"json", no_argument, NULL, 'J'},
+ {"json-stream", no_argument, NULL, OPT_JSON_STREAM},
+ {"json-stream-full-output", no_argument, NULL, OPT_JSON_STREAM_FULL_OUTPUT},
{"version", no_argument, NULL, 'v'},
{"server", no_argument, NULL, 's'},
{"client", required_argument, NULL, 'c'},
{"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},
+ {"server-bitrate-limit", required_argument, NULL, OPT_SERVER_BITRATE_LIMIT},
+ {"server-max-duration", required_argument, NULL, OPT_SERVER_MAX_DURATION},
{"time", required_argument, NULL, 't'},
{"bytes", required_argument, NULL, 'n'},
{"blockcount", required_argument, NULL, 'k'},
@@ -1050,9 +1129,9 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv)
{"bidir", no_argument, NULL, OPT_BIDIRECTIONAL},
{"window", required_argument, NULL, 'w'},
{"bind", required_argument, NULL, 'B'},
-#if defined(HAVE_SO_BINDTODEVICE)
+#if defined(CAN_BIND_TO_DEVICE)
{"bind-dev", required_argument, NULL, OPT_BIND_DEV},
-#endif /* HAVE_SO_BINDTODEVICE */
+#endif /* CAN_BIND_TO_DEVICE */
{"cport", required_argument, NULL, OPT_CLIENT_PORT},
{"set-mss", required_argument, NULL, 'M'},
{"no-delay", no_argument, NULL, 'N'},
@@ -1091,12 +1170,16 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv)
#if defined(HAVE_DONT_FRAGMENT)
{"dont-fragment", no_argument, NULL, OPT_DONT_FRAGMENT},
#endif /* HAVE_DONT_FRAGMENT */
+#if defined(HAVE_MSG_TRUNC)
+ {"skip-rx-copy", no_argument, NULL, OPT_SKIP_RX_COPY},
+#endif /* HAVE_MSG_TRUNC */
#if defined(HAVE_SSL)
{"username", required_argument, NULL, OPT_CLIENT_USERNAME},
{"rsa-public-key-path", required_argument, NULL, OPT_CLIENT_RSA_PUBLIC_KEY},
{"rsa-private-key-path", required_argument, NULL, OPT_SERVER_RSA_PRIVATE_KEY},
{"authorized-users-path", required_argument, NULL, OPT_SERVER_AUTHORIZED_USERS},
{"time-skew-threshold", required_argument, NULL, OPT_SERVER_SKEW_THRESHOLD},
+ {"use-pkcs1-padding", no_argument, NULL, OPT_USE_PKCS1_PADDING},
#endif /* HAVE_SSL */
{"fq-rate", required_argument, NULL, OPT_FQ_RATE},
{"pacing-timer", required_argument, NULL, OPT_PACING_TIMER},
@@ -1104,6 +1187,13 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv)
{"idle-timeout", required_argument, NULL, OPT_IDLE_TIMEOUT},
{"rcv-timeout", required_argument, NULL, OPT_RCV_TIMEOUT},
{"snd-timeout", required_argument, NULL, OPT_SND_TIMEOUT},
+#if defined(HAVE_TCP_KEEPALIVE)
+ {"cntl-ka", optional_argument, NULL, OPT_CNTL_KA},
+#endif /* HAVE_TCP_KEEPALIVE */
+#if defined(HAVE_IPPROTO_MPTCP)
+ {"mptcp", no_argument, NULL, 'm'},
+#endif
+ {"gsro", no_argument, NULL, OPT_GSRO},
{"debug", optional_argument, NULL, 'd'},
{"help", no_argument, NULL, 'h'},
{NULL, 0, NULL, 0}
@@ -1117,6 +1207,9 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv)
char* comma;
#endif /* HAVE_CPU_AFFINITY */
char* slash;
+#if defined(HAVE_TCP_KEEPALIVE)
+ char* slash2;
+#endif /* HAVE_TCP_KEEPALIVE */
char *p, *p1;
struct xbind_entry *xbe;
double farg;
@@ -1124,11 +1217,13 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv)
blksize = 0;
server_flag = client_flag = rate_flag = duration_flag = rcv_timeout_flag = snd_timeout_flag =0;
+ int gsro_flag = 0;
#if defined(HAVE_SSL)
char *client_username = NULL, *client_rsa_public_key = NULL, *server_rsa_private_key = NULL;
+ FILE *ptr_file;
#endif /* HAVE_SSL */
- while ((flag = getopt_long(argc, argv, "p:f:i:D1VJvsc:ub:t:n:k:l:P:Rw:B:M:N46S:L:ZO:F:A:T:C:dI:hX:", longopts, NULL)) != -1) {
+ while ((flag = getopt_long(argc, argv, "p:f:i:D1VJvsc:ub:t:n:k:l:P:Rw:B:M:N46S:L:ZO:F:A:T:C:dI:mhX:", longopts, NULL)) != -1) {
switch (flag) {
case 'p':
portno = atoi(optarg);
@@ -1182,6 +1277,13 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv)
case 'J':
test->json_output = 1;
break;
+ case OPT_JSON_STREAM:
+ test->json_output = 1;
+ test->json_stream = 1;
+ break;
+ case OPT_JSON_STREAM_FULL_OUTPUT:
+ test->json_stream_full_output = 1;
+ break;
case 'v':
printf("%s (cJSON %s)\n%s\n%s\n", version, cJSON_Version(), get_system_info(),
get_optional_features());
@@ -1193,7 +1295,7 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv)
}
iperf_set_test_role(test, 's');
break;
- case 'c':
+ case 'c': {
if (test->role == 's') {
i_errno = IESERVCLIENT;
return -1;
@@ -1201,18 +1303,22 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv)
iperf_set_test_role(test, 'c');
iperf_set_test_server_hostname(test, optarg);
- if (iperf_parse_hostname(test, optarg, &p, &p1)) {
-#if defined(HAVE_SO_BINDTODEVICE)
+ char *arg = strdup(optarg);
+ if (iperf_parse_hostname(test, arg, &p, &p1)) {
+#if defined(CAN_BIND_TO_DEVICE)
/* Get rid of the hostname we saved earlier. */
free(iperf_get_test_server_hostname(test));
iperf_set_test_server_hostname(test, p);
iperf_set_test_bind_dev(test, p1);
-#else /* HAVE_SO_BINDTODEVICE */
+#else /* CAN_BIND_TO_DEVICE */
+ free(arg);
i_errno = IEBINDDEVNOSUPPORT;
return -1;
-#endif /* HAVE_SO_BINDTODEVICE */
+#endif /* CAN_BIND_TO_DEVICE */
}
+ free(arg);
break;
+ }
case 'u':
set_protocol(test, Pudp);
client_flag = 1;
@@ -1230,6 +1336,9 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv)
case OPT_NUMSTREAMS:
#if defined(linux) || defined(__FreeBSD__)
test->settings->num_ostreams = unit_atoi(optarg);
+ if (i_errno != 0) {
+ return -1;
+ }
client_flag = 1;
#else /* linux */
i_errno = IEUNIMP;
@@ -1248,6 +1357,9 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv)
}
}
test->settings->rate = unit_atof_rate(optarg);
+ if (i_errno != 0) {
+ return -1;
+ }
rate_flag = 1;
client_flag = 1;
break;
@@ -1264,11 +1376,14 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv)
}
}
test->settings->bitrate_limit = unit_atof_rate(optarg);
+ if (i_errno != 0) {
+ return -1;
+ }
server_flag = 1;
break;
case 't':
test->duration = atoi(optarg);
- if (test->duration > MAX_TIME) {
+ if (test->duration > MAX_TIME || test->duration < 0) {
i_errno = IEDURATION;
return -1;
}
@@ -1277,14 +1392,23 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv)
break;
case 'n':
test->settings->bytes = unit_atoi(optarg);
+ if (i_errno != 0) {
+ return -1;
+ }
client_flag = 1;
break;
case 'k':
test->settings->blocks = unit_atoi(optarg);
+ if (i_errno != 0) {
+ return -1;
+ }
client_flag = 1;
break;
case 'l':
blksize = unit_atoi(optarg);
+ if (i_errno != 0) {
+ return -1;
+ }
client_flag = 1;
break;
case 'P':
@@ -1316,6 +1440,9 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv)
// Do sanity checks as double-precision floating point
// to avoid possible integer overflows.
farg = unit_atof(optarg);
+ if (i_errno != 0) {
+ return -1;
+ }
if (farg > (double) MAX_TCP_BUFFER) {
i_errno = IEBUFSIZE;
return -1;
@@ -1324,26 +1451,30 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv)
client_flag = 1;
break;
- case 'B':
+ case 'B': {
iperf_set_test_bind_address(test, optarg);
- if (iperf_parse_hostname(test, optarg, &p, &p1)) {
-#if defined(HAVE_SO_BINDTODEVICE)
+ char *arg = strdup(optarg);
+ if (iperf_parse_hostname(test, arg, &p, &p1)) {
+#if defined(CAN_BIND_TO_DEVICE)
/* Get rid of the hostname we saved earlier. */
free(iperf_get_test_bind_address(test));
iperf_set_test_bind_address(test, p);
iperf_set_test_bind_dev(test, p1);
-#else /* HAVE_SO_BINDTODEVICE */
+#else /* CAN_BIND_TO_DEVICE */
+ free(arg);
i_errno = IEBINDDEVNOSUPPORT;
return -1;
-#endif /* HAVE_SO_BINDTODEVICE */
+#endif /* CAN_BIND_TO_DEVICE */
}
+ free(arg);
break;
-#if defined (HAVE_SO_BINDTODEVICE)
+ }
+#if defined(CAN_BIND_TO_DEVICE)
case OPT_BIND_DEV:
iperf_set_test_bind_dev(test, optarg);
break;
-#endif /* HAVE_SO_BINDTODEVICE */
+#endif /* CAN_BIND_TO_DEVICE */
case OPT_CLIENT_PORT:
portno = atoi(optarg);
if (portno < 1 || portno > 65535) {
@@ -1443,7 +1574,7 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv)
break;
case 'O':
test->omit = atoi(optarg);
- if (test->omit < 0 || test->omit > 60) {
+ if (test->omit < 0 || test->omit > MAX_OMIT_TIME) {
i_errno = IEOMIT;
return -1;
}
@@ -1460,6 +1591,14 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv)
}
server_flag = 1;
break;
+ case OPT_SERVER_MAX_DURATION:
+ test->max_server_duration = atoi(optarg);
+ if (test->max_server_duration < 0 || test->max_server_duration > MAX_TIME) {
+ i_errno = IEDURATION;
+ return -1;
+ }
+ server_flag = 1;
+ break;
case OPT_RCV_TIMEOUT:
rcv_timeout_in = atoi(optarg);
if (rcv_timeout_in < MIN_NO_MSG_RCVD_TIMEOUT || rcv_timeout_in > MAX_TIME * SEC_TO_mS) {
@@ -1480,6 +1619,39 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv)
snd_timeout_flag = 1;
break;
#endif /* HAVE_TCP_USER_TIMEOUT */
+#if defined (HAVE_TCP_KEEPALIVE)
+ case OPT_CNTL_KA:
+ test->settings->cntl_ka = 1;
+ if (optarg) {
+ slash = strchr(optarg, '/');
+ if (slash) {
+ *slash = '\0';
+ ++slash;
+ slash2 = strchr(slash, '/');
+ if (slash2) {
+ *slash2 = '\0';
+ ++slash2;
+ if (strlen(slash2) > 0) {
+ test->settings->cntl_ka_count = atoi(slash2);
+ }
+ }
+ if (strlen(slash) > 0) {
+ test->settings->cntl_ka_interval = atoi(slash);
+ }
+ }
+ if (strlen(optarg) > 0) {
+ test->settings->cntl_ka_keepidle = atoi(optarg);
+ }
+ }
+ // Seems that at least in Windows WSL2, TCP keepalive retries full interval must be
+ // smaller than the idle interval. Otherwise, the keepalive message is sent only once.
+ if (test->settings->cntl_ka_keepidle &&
+ test->settings->cntl_ka_keepidle <= (test->settings->cntl_ka_count * test->settings->cntl_ka_interval)) {
+ i_errno = IECNTLKA;
+ return -1;
+ }
+ break;
+#endif /* HAVE_TCP_KEEPALIVE */
case 'A':
#if defined(HAVE_CPU_AFFINITY)
test->affinity = strtol(optarg, &endptr, 0);
@@ -1539,6 +1711,7 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv)
break;
case OPT_UDP_COUNTERS_64BIT:
test->udp_counters_64bit = 1;
+ client_flag = 1;
break;
case OPT_NO_FQ_SOCKET_PACING:
#if defined(HAVE_SO_MAX_PACING_RATE)
@@ -1553,6 +1726,9 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv)
case OPT_FQ_RATE:
#if defined(HAVE_SO_MAX_PACING_RATE)
test->settings->fqrate = unit_atof_rate(optarg);
+ if (i_errno != 0) {
+ return -1;
+ }
client_flag = 1;
#else /* HAVE_SO_MAX_PACING_RATE */
i_errno = IEUNIMP;
@@ -1568,12 +1744,15 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv)
#if defined(HAVE_SSL)
case OPT_CLIENT_USERNAME:
client_username = strdup(optarg);
+ client_flag = 1;
break;
case OPT_CLIENT_RSA_PUBLIC_KEY:
client_rsa_public_key = strdup(optarg);
+ client_flag = 1;
break;
case OPT_SERVER_RSA_PRIVATE_KEY:
server_rsa_private_key = strdup(optarg);
+ server_flag = 1;
break;
case OPT_SERVER_AUTHORIZED_USERS:
test->server_authorized_users = strdup(optarg);
@@ -1584,16 +1763,46 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv)
i_errno = IESKEWTHRESHOLD;
return -1;
}
+ server_flag = 1;
break;
+ case OPT_USE_PKCS1_PADDING:
+ test->use_pkcs1_padding = 1;
+ server_flag = 1;
+ break;
#endif /* HAVE_SSL */
+#if defined(HAVE_MSG_TRUNC)
+ case OPT_SKIP_RX_COPY:
+ test->settings->skip_rx_copy = 1;
+ client_flag = 1;
+ break;
+#endif /* HAVE_MSG_TRUNC */
case OPT_PACING_TIMER:
test->settings->pacing_timer = unit_atoi(optarg);
+ if (i_errno != 0) {
+ return -1;
+ }
client_flag = 1;
break;
case OPT_CONNECT_TIMEOUT:
test->settings->connect_timeout = unit_atoi(optarg);
+ if (i_errno != 0) {
+ return -1;
+ }
client_flag = 1;
break;
+#if defined(HAVE_IPPROTO_MPTCP)
+ case 'm':
+ set_protocol(test, Ptcp);
+ test->mptcp = 1;
+ break;
+#endif
+ case OPT_GSRO:
+ /* Enable GSO/GRO which is disabled by default */
+ /* Flag is available regardless of local support to allow client to request server to use it */
+ gsro_flag = 1;
+ test->settings->gso = 1;
+ test->settings->gro = 1;
+ break;
case 'h':
usage_long(stdout);
exit(0);
@@ -1613,6 +1822,21 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv)
i_errno = IECLIENTONLY;
return -1;
}
+ if (test->role == 's' && gsro_flag) {
+ i_errno = IECLIENTONLY;
+ return -1;
+ }
+
+ /* Show platform support warnings only after confirming we're in client mode */
+ if (gsro_flag) {
+#if !defined(HAVE_UDP_SEGMENT) && !defined(HAVE_UDP_GRO)
+ warning("--gsro requested but UDP GSO/GRO not supported on this client; will only be enabled on server if supported");
+#elif !defined(HAVE_UDP_SEGMENT)
+ warning("--gsro requested but UDP GSO not supported on this client; will be enabled on server if supported");
+#elif !defined(HAVE_UDP_GRO)
+ warning("--gsro requested but UDP GRO not supported on this client; will be enabled on server if supported");
+#endif
+ }
#if defined(HAVE_SSL)
@@ -1660,7 +1884,18 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv)
!(server_rsa_private_key && test->server_authorized_users)) {
i_errno = IESETSERVERAUTH;
return -1;
- } else if (test->role == 's' && server_rsa_private_key) {
+ }
+
+ if (test->role == 's' && test->server_authorized_users) {
+ ptr_file =fopen(test->server_authorized_users, "r");
+ if (!ptr_file) {
+ i_errno = IESERVERAUTHUSERS;
+ return -1;
+ }
+ fclose(ptr_file);
+ }
+
+ if (test->role == 's' && server_rsa_private_key) {
test->server_rsa_private_key = load_privkey_from_file(server_rsa_private_key);
if (test->server_rsa_private_key == NULL){
iperf_err(test, "%s\n", ERR_error_string(ERR_get_error(), NULL));
@@ -1678,8 +1913,12 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv)
#endif //HAVE_SSL
- // File cannot be transferred using UDP because of the UDP packets header (packet number, etc.)
- if(test->role == 'c' && test->diskfile_name != (char*) 0 && test->protocol->id == Pudp) {
+ /*
+ * File cannot be transferred using UDP because of the UDP packets
+ * header (packet number, etc.). Specifying Pudp here implies this is
+ * on the client side.
+ */
+ if (test->diskfile_name != (char*) 0 && test->protocol->id == Pudp) {
i_errno = IEUDPFILETRANSFER;
return -1;
}
@@ -1703,6 +1942,18 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv)
i_errno = IEUDPBLOCKSIZE;
return -1;
}
+
+ if (test->protocol->id == Pudp && test->settings->gso) {
+ test->settings->gso_dg_size = blksize;
+ /* use the multiple of datagram size for the best efficiency. */
+ if (test->settings->gso_dg_size > 0) {
+ test->settings->gso_bf_size = (test->settings->gso_bf_size / test->settings->gso_dg_size) * test->settings->gso_dg_size;
+ } else {
+ /* If gso_dg_size is 0 (unlimited bandwidth), use default UDP datagram size */
+ test->settings->gso_dg_size = DEFAULT_UDP_BLKSIZE;
+ }
+ }
+
test->settings->blksize = blksize;
if (!rate_flag)
@@ -1756,7 +2007,8 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv)
/* 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 ?
+ (test->settings->bitrate_limit_interval <= test->stats_interval ||
+ test->stats_interval == 0 ?
1 : round(test->settings->bitrate_limit_interval/test->stats_interval) );
}
@@ -1802,7 +2054,7 @@ int
iperf_set_send_state(struct iperf_test *test, signed char state)
{
if (test->ctrl_sck >= 0) {
- test->state = state;
+ iperf_set_test_state(test, state);
if (Nwrite(test->ctrl_sck, (char*) &state, sizeof(state), Ptcp) < 0) {
i_errno = IESENDMESSAGE;
return -1;
@@ -1817,19 +2069,73 @@ iperf_check_throttle(struct iperf_stream *sp, struct iperf_time *nowP)
struct iperf_time temp_time;
double seconds;
uint64_t bits_per_second;
+ int64_t missing_rate;
+ uint64_t bits_sent;
+
+#if defined(HAVE_CLOCK_NANOSLEEP) || defined(HAVE_NANOSLEEP)
+ struct timespec nanosleep_time;
+ int64_t time_to_green_light, delta_bits;
+ int ret;
+#endif /* HAVE_CLOCK_NANOSLEEP || HAVE_NANOSLEEP) */
+#if defined(HAVE_CLOCK_NANOSLEEP)
+ int64_t ns;
+#endif /* HAVE_CLOCK_NANOSLEEP */
if (sp->test->done || sp->test->settings->rate == 0)
return;
iperf_time_diff(&sp->result->start_time_fixed, nowP, &temp_time);
seconds = iperf_time_in_secs(&temp_time);
- bits_per_second = sp->result->bytes_sent * 8 / seconds;
- if (bits_per_second < sp->test->settings->rate) {
+ bits_sent = sp->result->bytes_sent * 8;
+ bits_per_second = bits_sent / seconds;
+ missing_rate = sp->test->settings->rate - bits_per_second;
+
+ if (missing_rate > 0) {
sp->green_light = 1;
- FD_SET(sp->socket, &sp->test->write_set);
} else {
sp->green_light = 0;
- FD_CLR(sp->socket, &sp->test->write_set);
}
+
+#if defined(HAVE_CLOCK_NANOSLEEP) || defined(HAVE_NANOSLEEP)
+ // If estimated time to next send is large enough, sleep instead of just CPU looping until green light is set
+ if (missing_rate < 0) {
+ delta_bits = bits_sent - (seconds * sp->test->settings->rate);
+ // Calculate time until next data send is required
+ time_to_green_light = (SEC_TO_NS * delta_bits / sp->test->settings->rate);
+ // Whether should wait before next send
+ if (time_to_green_light >= 0) {
+#if defined(HAVE_CLOCK_NANOSLEEP)
+ if (clock_gettime(CLOCK_MONOTONIC, &nanosleep_time) == 0) {
+ // Calculate absolute end of sleep time
+ ns = nanosleep_time.tv_nsec + time_to_green_light;
+ if (ns < SEC_TO_NS) {
+ nanosleep_time.tv_nsec = ns;
+ } else {
+ nanosleep_time.tv_sec += ns / SEC_TO_NS;
+ nanosleep_time.tv_nsec = ns % SEC_TO_NS;
+ }
+ // Sleep until average baud rate reaches the target value
+ while((ret = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &nanosleep_time, NULL)) == EINTR);
+ if (ret == 0) {
+ sp->green_light = 1;
+ }
+ }
+
+#else /* HAVE_NANOSLEEP */
+ nanosleep_time.tv_sec = 0;
+ // Sleep until average baud rate reaches the target value or interrupt / error
+ do {
+ // nansleep() time should be less than 1 sec
+ nanosleep_time.tv_nsec = (time_to_green_light >= SEC_TO_NS) ? SEC_TO_NS - 1 : time_to_green_light;
+ time_to_green_light -= nanosleep_time.tv_nsec;
+ ret = nanosleep(&nanosleep_time, NULL);
+ } while (ret == 0 && time_to_green_light > 0);
+ if (ret == 0) {
+ sp->green_light = 1;
+ }
+#endif /* HAVE_CLOCK_NANOSLEEP else HAVE_NANOSLEEP */
+ }
+ }
+#endif /* HAVE_CLOCK_NANOSLEEP || HAVE_NANOSLEEP */
}
/* Verify that average traffic is not greater than the specified limit */
@@ -1844,7 +2150,7 @@ iperf_check_total_rate(struct iperf_test *test, iperf_size_t last_interval_bytes
if (test->done || test->settings->bitrate_limit == 0) // Continue only if check should be done
return;
- /* Add last inetrval's transferred bytes to the array */
+ /* Add last interval's transferred 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;
@@ -1855,7 +2161,7 @@ iperf_check_total_rate(struct iperf_test *test, iperf_size_t last_interval_bytes
return;
/* Calculating total bytes traffic to be averaged */
- for (total_bytes = 0, i = 0; i < test->settings->bitrate_limit_stats_per_interval; i++) {
+ for (i = 0, total_bytes = 0; i < test->settings->bitrate_limit_stats_per_interval; i++) {
total_bytes += test->bitrate_limit_intervals_traffic_bytes[i];
}
@@ -1873,12 +2179,15 @@ iperf_check_total_rate(struct iperf_test *test, iperf_size_t last_interval_bytes
}
int
-iperf_send(struct iperf_test *test, fd_set *write_setP)
+iperf_send_mt(struct iperf_stream *sp)
{
- register int multisend, r, streams_active;
- register struct iperf_stream *sp;
+ register int multisend, r, message_sent;
+ register struct iperf_test *test = sp->test;
struct iperf_time now;
- int no_throttle_check;
+ int throttle_check_per_message;
+#if defined(HAVE_CLOCK_NANOSLEEP) || defined(HAVE_NANOSLEEP)
+ int throttle_check;
+#endif /* HAVE_CLOCK_NANOSLEEP, HAVE_NANOSLEEP */
/* Can we do multisend mode? */
if (test->settings->burst != 0)
@@ -1889,67 +2198,74 @@ iperf_send(struct iperf_test *test, fd_set *write_setP)
multisend = 1; /* nope */
/* Should bitrate throttle be checked for every send */
- no_throttle_check = test->settings->rate != 0 && test->settings->burst == 0;
-
- for (; multisend > 0; --multisend) {
- if (no_throttle_check)
- iperf_time_now(&now);
- streams_active = 0;
- SLIST_FOREACH(sp, &test->streams, streams) {
- if ((sp->green_light && sp->sender &&
- (write_setP == NULL || FD_ISSET(sp->socket, write_setP)))) {
+#if defined(HAVE_CLOCK_NANOSLEEP) || defined(HAVE_NANOSLEEP)
+ if (test->settings->rate != 0) {
+ throttle_check = 1;
+ if (test->settings->burst == 0)
+ throttle_check_per_message = 1;
+ else
+ throttle_check_per_message = 0;
+ } else {
+ throttle_check = 0;
+ throttle_check_per_message = 0;
+ }
+#else /* !HAVE_CLOCK_NANOSLEEP && !HAVE_NANOSLEEP */
+ throttle_check_per_message = test->settings->rate != 0 && test->settings->burst == 0;
+#endif /* HAVE_CLOCK_NANOSLEEP, HAVE_NANOSLEEP */
+
+ for (message_sent = 0; sp->green_light && multisend > 0; --multisend) {
+ // XXX If we hit one of these ending conditions maybe
+ // want to stop even trying to send something?
if (multisend > 1 && test->settings->bytes != 0 && test->bytes_sent >= test->settings->bytes)
break;
if (multisend > 1 && test->settings->blocks != 0 && test->blocks_sent >= test->settings->blocks)
break;
- if ((r = sp->snd(sp)) < 0) {
- if (r == NET_SOFTERROR)
- break;
- i_errno = IESTREAMWRITE;
- return r;
- }
- streams_active = 1;
- test->bytes_sent += r;
- if (!sp->pending_size)
- ++test->blocks_sent;
- if (no_throttle_check)
- iperf_check_throttle(sp, &now);
- }
- }
- if (!streams_active)
- break;
- }
- if (!no_throttle_check) { /* Throttle check if was not checked for each send */
+ if ((r = sp->snd(sp)) < 0) {
+ if (r == NET_SOFTERROR)
+ break;
+ i_errno = IESTREAMWRITE;
+ return r;
+ }
+ test->bytes_sent += r;
+ if (!sp->pending_size)
+ ++test->blocks_sent;
+ if (throttle_check_per_message) {
+ if (message_sent == 0)
+ iperf_time_now(&now);
+ iperf_check_throttle(sp, &now);
+ }
+ message_sent = 1;
+ }
+#if defined(HAVE_CLOCK_NANOSLEEP) || defined(HAVE_NANOSLEEP)
+ /* Should check if green light can be set, as pacing timer is not supported in this case */
+ if (throttle_check && (!throttle_check_per_message || message_sent == 0)) {
+#else /* !HAVE_CLOCK_NANOSLEEP && !HAVE_NANOSLEEP */
+ if (!throttle_check_per_message || message_sent == 0) { /* Throttle check if was not checked for each send */
+#endif /* HAVE_CLOCK_NANOSLEEP, HAVE_NANOSLEEP */
iperf_time_now(&now);
- SLIST_FOREACH(sp, &test->streams, streams)
- if (sp->sender)
- iperf_check_throttle(sp, &now);
+ iperf_check_throttle(sp, &now);
}
- if (write_setP != NULL)
- SLIST_FOREACH(sp, &test->streams, streams)
- if (FD_ISSET(sp->socket, write_setP))
- FD_CLR(sp->socket, write_setP);
-
return 0;
}
int
-iperf_recv(struct iperf_test *test, fd_set *read_setP)
+iperf_recv_mt(struct iperf_stream *sp)
{
int r;
- struct iperf_stream *sp;
+ struct iperf_test *test = sp->test;
- SLIST_FOREACH(sp, &test->streams, streams) {
- if (FD_ISSET(sp->socket, read_setP) && !sp->sender) {
if ((r = sp->rcv(sp)) < 0) {
i_errno = IESTREAMREAD;
return r;
}
- test->bytes_received += r;
- ++test->blocks_received;
- FD_CLR(sp->socket, read_setP);
- }
- }
+
+ /* Collect statistics only if receive did not timeout (e.g. `Nread()` may timeout).
+ * This is also important for `--rcv-timeout` to work properly.
+ */
+ if (r > 0) {
+ test->bytes_received += r;
+ ++test->blocks_received;
+ }
return 0;
}
@@ -1980,39 +2296,15 @@ iperf_init_test(struct iperf_test *test)
return 0;
}
-static void
-send_timer_proc(TimerClientData client_data, struct iperf_time *nowP)
-{
- struct iperf_stream *sp = client_data.p;
-
- /* All we do here is set or clear the flag saying that this stream may
- ** be sent to. The actual sending gets done in the send proc, after
- ** checking the flag.
- */
- iperf_check_throttle(sp, nowP);
-}
int
iperf_create_send_timers(struct iperf_test * test)
{
- struct iperf_time now;
+ // Note: No times for the multi-thread versions
struct iperf_stream *sp;
- TimerClientData cd;
- if (iperf_time_now(&now) < 0) {
- i_errno = IEINITTEST;
- return -1;
- }
SLIST_FOREACH(sp, &test->streams, streams) {
sp->green_light = 1;
- 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) {
- i_errno = IEINITTEST;
- return -1;
- }
- }
}
return 0;
}
@@ -2026,21 +2318,21 @@ int test_is_authorized(struct iperf_test *test){
if (test->settings->authtoken){
char *username = NULL, *password = NULL;
time_t ts;
- int rc = decode_auth_setting(test->debug, test->settings->authtoken, test->server_rsa_private_key, &username, &password, &ts);
+ int rc = decode_auth_setting(test->debug, test->settings->authtoken, test->server_rsa_private_key, &username, &password, &ts, test->use_pkcs1_padding);
if (rc) {
return -1;
}
int ret = check_authentication(username, password, ts, test->server_authorized_users, test->server_skew_threshold);
if (ret == 0){
if (test->debug) {
- iperf_printf(test, report_authentication_succeeded, username, ts);
+ iperf_printf(test, report_authentication_succeeded, username, (uint64_t)ts);
}
free(username);
free(password);
return 0;
} else {
if (test->debug) {
- iperf_printf(test, report_authentication_failed, ret, username, ts);
+ iperf_printf(test, report_authentication_failed, ret, username, (uint64_t)ts);
}
free(username);
free(password);
@@ -2060,7 +2352,6 @@ int
iperf_exchange_parameters(struct iperf_test *test)
{
int s;
- int32_t err;
if (test->role == 'c') {
@@ -2069,36 +2360,17 @@ iperf_exchange_parameters(struct iperf_test *test)
} else {
- if (get_parameters(test) < 0)
+ if (get_parameters(test) < 0) {
return -1;
+ }
#if defined(HAVE_SSL)
if (test_is_authorized(test) < 0){
- if (iperf_set_send_state(test, SERVER_ERROR) != 0)
- return -1;
- i_errno = IEAUTHTEST;
- err = htonl(i_errno);
- if (Nwrite(test->ctrl_sck, (char*) &err, sizeof(err), Ptcp) < 0) {
- i_errno = IECTRLWRITE;
- return -1;
- }
return -1;
}
#endif //HAVE_SSL
if ((s = test->protocol->listen(test)) < 0) {
- if (iperf_set_send_state(test, SERVER_ERROR) != 0)
- return -1;
- err = htonl(i_errno);
- if (Nwrite(test->ctrl_sck, (char*) &err, sizeof(err), Ptcp) < 0) {
- i_errno = IECTRLWRITE;
- return -1;
- }
- err = htonl(errno);
- if (Nwrite(test->ctrl_sck, (char*) &err, sizeof(err), Ptcp) < 0) {
- i_errno = IECTRLWRITE;
- return -1;
- }
return -1;
}
@@ -2172,6 +2444,10 @@ send_parameters(struct iperf_test *test)
cJSON_AddTrueToObject(j, "reverse");
if (test->bidirectional)
cJSON_AddTrueToObject(j, "bidirectional");
+#if defined(HAVE_IPPROTO_MPTCP)
+ if (test->mptcp)
+ cJSON_AddTrueToObject(j, "mptcp");
+#endif
if (test->settings->socket_bufsize)
cJSON_AddNumberToObject(j, "window", test->settings->socket_bufsize);
if (test->settings->blksize)
@@ -2184,6 +2460,16 @@ send_parameters(struct iperf_test *test)
cJSON_AddNumberToObject(j, "pacing_timer", test->settings->pacing_timer);
if (test->settings->burst)
cJSON_AddNumberToObject(j, "burst", test->settings->burst);
+
+ /* Send UDP GSO/GRO settings from client to server */
+ /* Always send these fields to allow server to use GSO/GRO even if client doesn't support it */
+ if (test->protocol->id == Pudp) {
+ cJSON_AddNumberToObject(j, "gso", test->settings->gso);
+ cJSON_AddNumberToObject(j, "gso_dg_size", test->settings->gso_dg_size);
+ cJSON_AddNumberToObject(j, "gso_bf_size", test->settings->gso_bf_size);
+ cJSON_AddNumberToObject(j, "gro", test->settings->gro);
+ cJSON_AddNumberToObject(j, "gro_bf_size", test->settings->gro_bf_size);
+ }
if (test->settings->tos)
cJSON_AddNumberToObject(j, "TOS", test->settings->tos);
if (test->settings->flowlabel)
@@ -2211,7 +2497,7 @@ send_parameters(struct iperf_test *test)
#if defined(HAVE_SSL)
/* Send authentication parameters */
if (test->settings->client_username && test->settings->client_password && test->settings->client_rsa_pubkey){
- int rc = encode_auth_setting(test->settings->client_username, test->settings->client_password, test->settings->client_rsa_pubkey, &test->settings->authtoken);
+ int rc = encode_auth_setting(test->settings->client_username, test->settings->client_password, test->settings->client_rsa_pubkey, &test->settings->authtoken, test->use_pkcs1_padding);
if (rc) {
cJSON_Delete(j);
@@ -2222,6 +2508,8 @@ send_parameters(struct iperf_test *test)
cJSON_AddStringToObject(j, "authtoken", test->settings->authtoken);
}
#endif // HAVE_SSL
+ if (test->settings->skip_rx_copy)
+ cJSON_AddNumberToObject(j, "skip_rx_copy", test->settings->skip_rx_copy);
cJSON_AddStringToObject(j, "client_version", IPERF_VERSION);
if (test->debug) {
@@ -2248,7 +2536,7 @@ get_parameters(struct iperf_test *test)
cJSON *j;
cJSON *j_p;
- j = JSON_read(test->ctrl_sck);
+ j = JSON_read(test->ctrl_sck, MAX_PARAMS_JSON_STRING);
if (j == NULL) {
i_errno = IERECVPARAMS;
r = -1;
@@ -2260,79 +2548,140 @@ get_parameters(struct iperf_test *test)
cJSON_free(str);
}
- if ((j_p = cJSON_GetObjectItem(j, "tcp")) != NULL)
+ if ((j_p = iperf_cJSON_GetObjectItemType(j, "tcp", cJSON_True)) != NULL)
set_protocol(test, Ptcp);
- if ((j_p = cJSON_GetObjectItem(j, "udp")) != NULL)
- set_protocol(test, Pudp);
- if ((j_p = cJSON_GetObjectItem(j, "sctp")) != NULL)
+ if ((j_p = iperf_cJSON_GetObjectItemType(j, "udp", cJSON_True)) != NULL) {
+ /* Disallow UDP transfers if we already are to/from a file */
+ if (test->diskfile_name != NULL) {
+ i_errno = IEUDPFILETRANSFER;
+ r = -1;
+ }
+ else {
+ /* Not to/from a file, set UDP protocol as intended*/
+ set_protocol(test, Pudp);
+ }
+ }
+ if ((j_p = iperf_cJSON_GetObjectItemType(j, "sctp", cJSON_True)) != NULL)
set_protocol(test, Psctp);
- if ((j_p = cJSON_GetObjectItem(j, "omit")) != NULL)
+ if ((j_p = iperf_cJSON_GetObjectItemType(j, "omit", cJSON_Number)) != NULL)
test->omit = j_p->valueint;
- if ((j_p = cJSON_GetObjectItem(j, "server_affinity")) != NULL)
+ if ((j_p = iperf_cJSON_GetObjectItemType(j, "server_affinity", cJSON_Number)) != NULL)
test->server_affinity = j_p->valueint;
- if ((j_p = cJSON_GetObjectItem(j, "time")) != NULL)
+ if ((j_p = iperf_cJSON_GetObjectItemType(j, "time", cJSON_Number)) != NULL)
test->duration = j_p->valueint;
test->settings->bytes = 0;
- if ((j_p = cJSON_GetObjectItem(j, "num")) != NULL)
+ if ((j_p = iperf_cJSON_GetObjectItemType(j, "num", cJSON_Number)) != NULL)
test->settings->bytes = j_p->valueint;
test->settings->blocks = 0;
- if ((j_p = cJSON_GetObjectItem(j, "blockcount")) != NULL)
+ if ((j_p = iperf_cJSON_GetObjectItemType(j, "blockcount", cJSON_Number)) != NULL)
test->settings->blocks = j_p->valueint;
- if ((j_p = cJSON_GetObjectItem(j, "MSS")) != NULL)
+ if ((j_p = iperf_cJSON_GetObjectItemType(j, "MSS", cJSON_Number)) != NULL)
test->settings->mss = j_p->valueint;
- if ((j_p = cJSON_GetObjectItem(j, "nodelay")) != NULL)
+ if ((j_p = iperf_cJSON_GetObjectItemType(j, "nodelay", cJSON_True)) != NULL)
test->no_delay = 1;
- if ((j_p = cJSON_GetObjectItem(j, "parallel")) != NULL)
+ if ((j_p = iperf_cJSON_GetObjectItemType(j, "parallel", cJSON_Number)) != NULL)
test->num_streams = j_p->valueint;
- if ((j_p = cJSON_GetObjectItem(j, "reverse")) != NULL)
+ if ((j_p = iperf_cJSON_GetObjectItemType(j, "reverse", cJSON_True)) != NULL)
iperf_set_test_reverse(test, 1);
- if ((j_p = cJSON_GetObjectItem(j, "bidirectional")) != NULL)
+ if ((j_p = iperf_cJSON_GetObjectItemType(j, "bidirectional", cJSON_True)) != NULL)
iperf_set_test_bidirectional(test, 1);
- if ((j_p = cJSON_GetObjectItem(j, "window")) != NULL)
+#if defined(HAVE_IPPROTO_MPTCP)
+ if ((j_p = iperf_cJSON_GetObjectItemType(j, "mptcp", cJSON_True)) != NULL)
+ test->mptcp = 1;
+#endif
+ if ((j_p = iperf_cJSON_GetObjectItemType(j, "window", cJSON_Number)) != NULL)
test->settings->socket_bufsize = j_p->valueint;
- if ((j_p = cJSON_GetObjectItem(j, "len")) != NULL)
+ if ((j_p = iperf_cJSON_GetObjectItemType(j, "len", cJSON_Number)) != NULL)
test->settings->blksize = j_p->valueint;
- if ((j_p = cJSON_GetObjectItem(j, "bandwidth")) != NULL)
+
+ /* Accept UDP GSO/GRO settings provided by the client */
+ /* Always accept these fields to allow server to use GSO/GRO based on its own support */
+ if ((j_p = iperf_cJSON_GetObjectItemType(j, "gso", cJSON_Number)) != NULL)
+ test->settings->gso = j_p->valueint;
+ if ((j_p = iperf_cJSON_GetObjectItemType(j, "gso_dg_size", cJSON_Number)) != NULL)
+ test->settings->gso_dg_size = j_p->valueint;
+ if ((j_p = iperf_cJSON_GetObjectItemType(j, "gso_bf_size", cJSON_Number)) != NULL)
+ test->settings->gso_bf_size = j_p->valueint;
+
+ /* Backward-compatibility: If client didn't send GSO params, derive from blksize. */
+ if (test->protocol->id == Pudp && test->settings->gso == 1 && test->settings->gso_dg_size == 0) {
+ test->settings->gso_dg_size = test->settings->blksize;
+ if (test->settings->gso_dg_size > 0) {
+ test->settings->gso_bf_size = (test->settings->gso_bf_size / test->settings->gso_dg_size) * test->settings->gso_dg_size;
+ } else {
+ test->settings->gso_dg_size = DEFAULT_UDP_BLKSIZE;
+ }
+ }
+
+ if ((j_p = iperf_cJSON_GetObjectItemType(j, "gro", cJSON_Number)) != NULL)
+ test->settings->gro = j_p->valueint;
+ if ((j_p = iperf_cJSON_GetObjectItemType(j, "gro_bf_size", cJSON_Number)) != NULL)
+ test->settings->gro_bf_size = j_p->valueint;
+
+ if ((j_p = iperf_cJSON_GetObjectItemType(j, "bandwidth", cJSON_Number)) != NULL)
test->settings->rate = j_p->valueint;
- if ((j_p = cJSON_GetObjectItem(j, "fqrate")) != NULL)
+ if ((j_p = iperf_cJSON_GetObjectItemType(j, "fqrate", cJSON_Number)) != NULL)
test->settings->fqrate = j_p->valueint;
- if ((j_p = cJSON_GetObjectItem(j, "pacing_timer")) != NULL)
+ if ((j_p = iperf_cJSON_GetObjectItemType(j, "pacing_timer", cJSON_Number)) != NULL)
test->settings->pacing_timer = j_p->valueint;
- if ((j_p = cJSON_GetObjectItem(j, "burst")) != NULL)
+ if ((j_p = iperf_cJSON_GetObjectItemType(j, "burst", cJSON_Number)) != NULL)
test->settings->burst = j_p->valueint;
- if ((j_p = cJSON_GetObjectItem(j, "TOS")) != NULL)
+ if ((j_p = iperf_cJSON_GetObjectItemType(j, "TOS", cJSON_Number)) != NULL)
test->settings->tos = j_p->valueint;
- if ((j_p = cJSON_GetObjectItem(j, "flowlabel")) != NULL)
+ if ((j_p = iperf_cJSON_GetObjectItemType(j, "flowlabel", cJSON_Number)) != NULL)
test->settings->flowlabel = j_p->valueint;
- if ((j_p = cJSON_GetObjectItem(j, "title")) != NULL)
+ if ((j_p = iperf_cJSON_GetObjectItemType(j, "title", cJSON_String)) != NULL)
test->title = strdup(j_p->valuestring);
- if ((j_p = cJSON_GetObjectItem(j, "extra_data")) != NULL)
+ if ((j_p = iperf_cJSON_GetObjectItemType(j, "extra_data", cJSON_String)) != NULL)
test->extra_data = strdup(j_p->valuestring);
- if ((j_p = cJSON_GetObjectItem(j, "congestion")) != NULL)
+ if ((j_p = iperf_cJSON_GetObjectItemType(j, "congestion", cJSON_String)) != NULL)
test->congestion = strdup(j_p->valuestring);
- if ((j_p = cJSON_GetObjectItem(j, "congestion_used")) != NULL)
+ if ((j_p = iperf_cJSON_GetObjectItemType(j, "congestion_used", cJSON_String)) != NULL)
test->congestion_used = strdup(j_p->valuestring);
- if ((j_p = cJSON_GetObjectItem(j, "get_server_output")) != NULL)
+ if ((j_p = iperf_cJSON_GetObjectItemType(j, "get_server_output", cJSON_Number)) != NULL)
iperf_set_test_get_server_output(test, 1);
- if ((j_p = cJSON_GetObjectItem(j, "udp_counters_64bit")) != NULL)
+ if ((j_p = iperf_cJSON_GetObjectItemType(j, "udp_counters_64bit", cJSON_Number)) != NULL)
iperf_set_test_udp_counters_64bit(test, 1);
- if ((j_p = cJSON_GetObjectItem(j, "repeating_payload")) != NULL)
+ if ((j_p = iperf_cJSON_GetObjectItemType(j, "repeating_payload", cJSON_Number)) != NULL)
test->repeating_payload = 1;
- if ((j_p = cJSON_GetObjectItem(j, "zerocopy")) != NULL)
+ if ((j_p = iperf_cJSON_GetObjectItemType(j, "zerocopy", cJSON_Number)) != NULL)
test->zerocopy = j_p->valueint;
#if defined(HAVE_DONT_FRAGMENT)
- if ((j_p = cJSON_GetObjectItem(j, "dont_fragment")) != NULL)
+ if ((j_p = iperf_cJSON_GetObjectItemType(j, "dont_fragment", cJSON_Number)) != NULL)
test->settings->dont_fragment = j_p->valueint;
#endif /* HAVE_DONT_FRAGMENT */
#if defined(HAVE_SSL)
- if ((j_p = cJSON_GetObjectItem(j, "authtoken")) != NULL)
+ if ((j_p = iperf_cJSON_GetObjectItemType(j, "authtoken", cJSON_String)) != NULL)
test->settings->authtoken = strdup(j_p->valuestring);
#endif //HAVE_SSL
+ if ((j_p = cJSON_GetObjectItem(j, "skip_rx_copy")) != NULL)
+ test->settings->skip_rx_copy = j_p->valueint;
if (test->mode && test->protocol->id == Ptcp && has_tcpinfo_retransmits())
test->sender_has_retransmits = 1;
if (test->settings->rate)
cJSON_AddNumberToObject(test->json_start, "target_bitrate", test->settings->rate);
cJSON_Delete(j);
+
+ /* Ensure that the client does not request to run longer than the server's configured max */
+ if ((test->max_server_duration > 0) && (((test->duration + test->omit) > test->max_server_duration) || (test->duration == 0))) {
+ i_errno = IEMAXSERVERTESTDURATIONEXCEEDED;
+ r = -1;
+ }
+
+
+ /* Ensure that total requested data rate is not above the server's limit */
+ iperf_size_t total_requested_rate = test->num_streams * test->settings->rate * (test->mode == BIDIRECTIONAL? 2 : 1);
+ if (test->settings->bitrate_limit && total_requested_rate > test->settings->bitrate_limit) {
+ i_errno = IETOTALRATE;
+ r = -1;
+ }
+
+ total_requested_rate = test->num_streams * test->settings->fqrate * (test->mode == BIDIRECTIONAL? 2 : 1);
+ if (test->settings->bitrate_limit && total_requested_rate > test->settings->bitrate_limit) {
+ i_errno = IETOTALRATE;
+ r = -1;
+ }
+
}
return r;
}
@@ -2418,7 +2767,9 @@ send_results(struct iperf_test *test)
cJSON_AddNumberToObject(j_stream, "retransmits", retransmits);
cJSON_AddNumberToObject(j_stream, "jitter", sp->jitter);
cJSON_AddNumberToObject(j_stream, "errors", sp->cnt_error);
+ cJSON_AddNumberToObject(j_stream, "omitted_errors", sp->omitted_cnt_error);
cJSON_AddNumberToObject(j_stream, "packets", sp->packet_count);
+ cJSON_AddNumberToObject(j_stream, "omitted_packets", sp->omitted_packet_count);
iperf_time_diff(&sp->result->start_time, &sp->result->start_time, &temp_time);
start_time = iperf_time_in_secs(&temp_time);
@@ -2465,24 +2816,29 @@ get_results(struct iperf_test *test)
cJSON *j_retransmits;
cJSON *j_jitter;
cJSON *j_errors;
+ cJSON *j_omitted_errors;
cJSON *j_packets;
+ cJSON *j_omitted_packets;
cJSON *j_server_output;
cJSON *j_start_time, *j_end_time;
- int sid, cerror, pcount;
+ int sid;
+ int64_t cerror, pcount;
+ // Init as seems to not be au-initialized to 0 by default, maybe because later init is only under if statement
+ int64_t omitted_cerror = 0, omitted_pcount = 0;
double jitter;
iperf_size_t bytes_transferred;
int retransmits;
struct iperf_stream *sp;
- j = JSON_read(test->ctrl_sck);
+ j = JSON_read(test->ctrl_sck, 0);
if (j == NULL) {
i_errno = IERECVRESULTS;
r = -1;
} else {
- j_cpu_util_total = cJSON_GetObjectItem(j, "cpu_util_total");
- j_cpu_util_user = cJSON_GetObjectItem(j, "cpu_util_user");
- j_cpu_util_system = cJSON_GetObjectItem(j, "cpu_util_system");
- j_sender_has_retransmits = cJSON_GetObjectItem(j, "sender_has_retransmits");
+ j_cpu_util_total = iperf_cJSON_GetObjectItemType(j, "cpu_util_total", cJSON_Number);
+ j_cpu_util_user = iperf_cJSON_GetObjectItemType(j, "cpu_util_user", cJSON_Number);
+ j_cpu_util_system = iperf_cJSON_GetObjectItemType(j, "cpu_util_system", cJSON_Number);
+ j_sender_has_retransmits = iperf_cJSON_GetObjectItemType(j, "sender_has_retransmits", cJSON_Number);
if (j_cpu_util_total == NULL || j_cpu_util_user == NULL || j_cpu_util_system == NULL || j_sender_has_retransmits == NULL) {
i_errno = IERECVRESULTS;
r = -1;
@@ -2504,7 +2860,7 @@ get_results(struct iperf_test *test)
else if ( test->mode == BIDIRECTIONAL )
test->other_side_has_retransmits = result_has_retransmits;
- j_streams = cJSON_GetObjectItem(j, "streams");
+ j_streams = iperf_cJSON_GetObjectItemType(j, "streams", cJSON_Array);
if (j_streams == NULL) {
i_errno = IERECVRESULTS;
r = -1;
@@ -2516,17 +2872,23 @@ get_results(struct iperf_test *test)
i_errno = IERECVRESULTS;
r = -1;
} else {
- j_id = cJSON_GetObjectItem(j_stream, "id");
- j_bytes = cJSON_GetObjectItem(j_stream, "bytes");
- j_retransmits = cJSON_GetObjectItem(j_stream, "retransmits");
- j_jitter = cJSON_GetObjectItem(j_stream, "jitter");
- j_errors = cJSON_GetObjectItem(j_stream, "errors");
- j_packets = cJSON_GetObjectItem(j_stream, "packets");
- j_start_time = cJSON_GetObjectItem(j_stream, "start_time");
- j_end_time = cJSON_GetObjectItem(j_stream, "end_time");
+ j_id = iperf_cJSON_GetObjectItemType(j_stream, "id", cJSON_Number);
+ j_bytes = iperf_cJSON_GetObjectItemType(j_stream, "bytes", cJSON_Number);
+ j_retransmits = iperf_cJSON_GetObjectItemType(j_stream, "retransmits", cJSON_Number);
+ j_jitter = iperf_cJSON_GetObjectItemType(j_stream, "jitter", cJSON_Number);
+ j_errors = iperf_cJSON_GetObjectItemType(j_stream, "errors", cJSON_Number);
+ j_omitted_errors = iperf_cJSON_GetObjectItemType(j_stream, "omitted_errors", cJSON_Number);
+ j_packets = iperf_cJSON_GetObjectItemType(j_stream, "packets", cJSON_Number);
+ j_omitted_packets = iperf_cJSON_GetObjectItemType(j_stream, "omitted_packets", cJSON_Number);
+ j_start_time = iperf_cJSON_GetObjectItemType(j_stream, "start_time", cJSON_Number);
+ j_end_time = iperf_cJSON_GetObjectItemType(j_stream, "end_time", cJSON_Number);
if (j_id == NULL || j_bytes == NULL || j_retransmits == NULL || j_jitter == NULL || j_errors == NULL || j_packets == NULL) {
i_errno = IERECVRESULTS;
r = -1;
+ } else if ( (j_omitted_errors == NULL && j_omitted_packets != NULL) || (j_omitted_errors != NULL && j_omitted_packets == NULL) ) {
+ /* For backward compatibility allow to not receive "omitted" statistics */
+ i_errno = IERECVRESULTS;
+ r = -1;
} else {
sid = j_id->valueint;
bytes_transferred = j_bytes->valueint;
@@ -2534,6 +2896,10 @@ get_results(struct iperf_test *test)
jitter = j_jitter->valuedouble;
cerror = j_errors->valueint;
pcount = j_packets->valueint;
+ if (j_omitted_packets != NULL) {
+ omitted_cerror = j_omitted_errors->valueint;
+ omitted_pcount = j_omitted_packets->valueint;
+ }
SLIST_FOREACH(sp, &test->streams, streams)
if (sp->id == sid) break;
if (sp == NULL) {
@@ -2545,6 +2911,18 @@ get_results(struct iperf_test *test)
sp->cnt_error = cerror;
sp->peer_packet_count = pcount;
sp->result->bytes_received = bytes_transferred;
+ if (j_omitted_packets != NULL) {
+ sp->omitted_cnt_error = omitted_cerror;
+ sp->peer_omitted_packet_count = omitted_pcount;
+ } else {
+ sp->peer_omitted_packet_count = sp->omitted_packet_count;
+ if (sp->peer_omitted_packet_count > 0) {
+ /* -1 indicates unknown error count since it includes the omitted count */
+ sp->omitted_cnt_error = (sp->cnt_error > 0) ? -1 : 0;
+ } else {
+ sp->omitted_cnt_error = sp->cnt_error;
+ }
+ }
/*
* We have to handle the possibility that
* start_time and end_time might not be
@@ -2564,6 +2942,11 @@ get_results(struct iperf_test *test)
sp->peer_packet_count = pcount;
sp->result->bytes_sent = bytes_transferred;
sp->result->stream_retrans = retransmits;
+ if (j_omitted_packets != NULL) {
+ sp->peer_omitted_packet_count = omitted_pcount;
+ } else {
+ sp->peer_omitted_packet_count = sp->peer_packet_count;
+ }
if (j_start_time && j_end_time) {
sp->result->sender_time = j_end_time->valuedouble - j_start_time->valuedouble;
}
@@ -2587,7 +2970,7 @@ get_results(struct iperf_test *test)
}
else {
/* No JSON, look for textual output. Make a copy of the text for later. */
- j_server_output = cJSON_GetObjectItem(j, "server_output_text");
+ j_server_output = iperf_cJSON_GetObjectItemType(j, "server_output_text", cJSON_String);
if (j_server_output != NULL) {
test->server_output_text = strdup(j_server_output->valuestring);
}
@@ -2596,7 +2979,7 @@ get_results(struct iperf_test *test)
}
}
- j_remote_congestion_used = cJSON_GetObjectItem(j, "congestion_used");
+ j_remote_congestion_used = iperf_cJSON_GetObjectItemType(j, "congestion_used", cJSON_String);
if (j_remote_congestion_used != NULL) {
test->remote_congestion_used = strdup(j_remote_congestion_used->valuestring);
}
@@ -2635,44 +3018,98 @@ JSON_write(int fd, cJSON *json)
/*************************************************************/
static cJSON *
-JSON_read(int fd)
+JSON_read(int fd, int max_size)
{
uint32_t hsize, nsize;
+ size_t strsize;
char *str;
cJSON *json = NULL;
int rc;
+ char msg_buf[WARN_STR_LEN * 2];
/*
* Read a four-byte integer, which is the length of the JSON to follow.
* Then read the JSON into a buffer and parse it. Return a parsed JSON
* structure, NULL if there was an error.
*/
- if (Nread(fd, (char*) &nsize, sizeof(nsize), Ptcp) >= 0) {
- hsize = ntohl(nsize);
- /* Allocate a buffer to hold the JSON */
- str = (char *) calloc(sizeof(char), hsize+1); /* +1 for trailing null */
- if (str != NULL) {
- rc = Nread(fd, str, hsize, Ptcp);
- if (rc >= 0) {
- /*
- * We should be reading in the number of bytes corresponding to the
- * length in that 4-byte integer. If we don't the socket might have
- * prematurely closed. Only do the JSON parsing if we got the
- * correct number of bytes.
- */
- if (rc == hsize) {
- json = cJSON_Parse(str);
- }
- else {
- printf("WARNING: Size of data read does not correspond to offered length\n");
- }
- }
+ rc = Nread(fd, (char*) &nsize, sizeof(nsize), Ptcp);
+ if (rc == sizeof(nsize)) {
+ hsize = ntohl(nsize);
+ if (hsize > 0 && (max_size == 0 || hsize <= max_size)) {
+ /* Allocate a buffer to hold the JSON */
+ strsize = hsize + 1; /* +1 for trailing NULL */
+ if (strsize) {
+ str = (char *) calloc(sizeof(char), strsize);
+ if (str != NULL) {
+ rc = Nread(fd, str, hsize, Ptcp);
+ if (rc >= 0) {
+ /*
+ * We should be reading in the number of bytes corresponding to the
+ * length in that 4-byte integer. If we don't the socket might have
+ * prematurely closed. Only do the JSON parsing if we got the
+ * correct number of bytes.
+ */
+ if (rc == hsize) {
+ json = cJSON_Parse(str);
+ }
+ else {
+ snprintf(msg_buf, sizeof(msg_buf), "JSON size of data read does not correspond to offered length - expected %d bytes but received %d; errno=%d", hsize, rc, errno);
+ warning(msg_buf);
+ }
+ }
+ else {
+ snprintf(msg_buf, sizeof(msg_buf), "JSON data read failed; errno=%d", errno);
+ warning(msg_buf);
+ }
+ free(str);
+ }
+ }
+ }
+ else {
+ snprintf(msg_buf, sizeof(msg_buf), "JSON data length overflow - %d bytes JSON size is not allowed", hsize);
+ warning(msg_buf);
}
- free(str);
+ }
+ else {
+ snprintf(msg_buf, sizeof(msg_buf), "Failed to read JSON data size - read returned %d; errno=%d", rc, errno);
+ warning(msg_buf);
}
return json;
}
+/*************************************************************/
+/**
+ * JSONStream_Output - outputs an obj as event without disturbing it
+ */
+
+static int
+JSONStream_Output(struct iperf_test * test, const char * event_name, cJSON * obj)
+{
+ cJSON *event = cJSON_CreateObject();
+ if (!event)
+ return -1;
+ cJSON_AddStringToObject(event, "event", event_name);
+ cJSON_AddItemReferenceToObject(event, "data", obj);
+ char *str = cJSON_PrintUnformatted(event);
+ if (str == NULL)
+ return -1;
+ if (test->json_callback != NULL) {
+ (test->json_callback)(test, str);
+ } else {
+ if (pthread_mutex_lock(&(test->print_mutex)) != 0) {
+ perror("iperf_json_finish: pthread_mutex_lock");
+ }
+ fprintf(test->outfile, "%s\n", str);
+ if (pthread_mutex_unlock(&(test->print_mutex)) != 0) {
+ perror("iperf_json_finish: pthread_mutex_unlock");
+ }
+ }
+ iflush(test);
+ cJSON_free(str);
+ cJSON_Delete(event);
+ return 0;
+}
+
/*************************************************************/
/**
* add_to_interval_list -- adds new interval to the interval_list
@@ -2683,6 +3120,14 @@ add_to_interval_list(struct iperf_stream_result * rp, struct iperf_interval_resu
{
struct iperf_interval_results *irp;
+ /* Only the last interval result is needed, so removing last old entry to reduce memory consumption */
+ if (!TAILQ_EMPTY(&rp->interval_results) &&
+ (irp = TAILQ_LAST(&rp->interval_results, irlisthead)) != NULL
+ ) {
+ TAILQ_REMOVE(&rp->interval_results, irp, irlistentries);
+ free(irp);
+ }
+
irp = (struct iperf_interval_results *) malloc(sizeof(struct iperf_interval_results));
memcpy(irp, new, sizeof(struct iperf_interval_results));
TAILQ_INSERT_TAIL(&rp->interval_results, irp, irlistentries);
@@ -2732,6 +3177,7 @@ struct iperf_test *
iperf_new_test()
{
struct iperf_test *test;
+ int rc;
test = (struct iperf_test *) malloc(sizeof(struct iperf_test));
if (!test) {
@@ -2741,6 +3187,21 @@ iperf_new_test()
/* initialize everything to zero */
memset(test, 0, sizeof(struct iperf_test));
+ /* Initialize mutex for printing output */
+ pthread_mutexattr_t mutexattr;
+ pthread_mutexattr_init(&mutexattr);
+ rc = pthread_mutexattr_settype(&mutexattr, PTHREAD_MUTEX_ERRORCHECK);
+ if (rc != 0) {
+ errno = rc;
+ perror("iperf_new_test: pthread_mutexattr_settype");
+ }
+
+ if (pthread_mutex_init(&(test->print_mutex), &mutexattr) != 0) {
+ perror("iperf_new_test: pthread_mutex_init");
+ }
+
+ pthread_mutexattr_destroy(&mutexattr);
+
test->settings = (struct iperf_settings *) malloc(sizeof(struct iperf_settings));
if (!test->settings) {
free(test);
@@ -2756,7 +3217,7 @@ iperf_new_test()
i_errno = IENEWTEST;
return NULL;
}
- memset(test->bitrate_limit_intervals_traffic_bytes, 0, sizeof(sizeof(iperf_size_t) * MAX_INTERVAL));
+ memset(test->bitrate_limit_intervals_traffic_bytes, 0, sizeof(iperf_size_t) * MAX_INTERVAL);
/* By default all output goes to stdout */
test->outfile = stdout;
@@ -2832,6 +3293,12 @@ iperf_defaults(struct iperf_test *testp)
testp->settings->fqrate = 0;
testp->settings->pacing_timer = DEFAULT_PACING_TIMER;
testp->settings->burst = 0;
+ /* Always initialize GSO/GRO fields to allow client-server negotiation */
+ testp->settings->gso = 0; /* Disable GSO by default, enabled via --gsro */
+ testp->settings->gso_dg_size = 0;
+ testp->settings->gso_bf_size = GSO_BF_MAX_SIZE;
+ testp->settings->gro = 0; /* Disable GRO by default, enabled via --gsro */
+ testp->settings->gro_bf_size = GRO_BF_MAX_SIZE;
testp->settings->mss = 0;
testp->settings->bytes = 0;
testp->settings->blocks = 0;
@@ -2839,6 +3306,14 @@ iperf_defaults(struct iperf_test *testp)
testp->settings->rcv_timeout.secs = DEFAULT_NO_MSG_RCVD_TIMEOUT / SEC_TO_mS;
testp->settings->rcv_timeout.usecs = (DEFAULT_NO_MSG_RCVD_TIMEOUT % SEC_TO_mS) * mS_TO_US;
testp->zerocopy = 0;
+ testp->settings->skip_rx_copy = 0;
+ testp->settings->cntl_ka = 0;
+ testp->settings->cntl_ka_keepidle = 0;
+ testp->settings->cntl_ka_interval = 0;
+ testp->settings->cntl_ka_count = 0;
+
+ testp->json_callback = NULL;
+
memset(testp->cookie, 0, COOKIE_SIZE);
@@ -2994,6 +3469,14 @@ iperf_free_test(struct iperf_test *test)
free(prot);
}
+ /* Destroy print mutex. iperf_printf() doesn't work after this point */
+ int rc;
+ rc = pthread_mutex_destroy(&(test->print_mutex));
+ if (rc != 0) {
+ errno = rc;
+ perror("iperf_free_test: pthread_mutex_destroy");
+ }
+
if (test->logfile) {
free(test->logfile);
test->logfile = NULL;
@@ -3077,6 +3560,9 @@ iperf_reset_test(struct iperf_test *test)
SLIST_INIT(&test->streams);
+ if (test->congestion)
+ free(test->congestion);
+ test->congestion = NULL;
if (test->remote_congestion_used)
free(test->remote_congestion_used);
test->remote_congestion_used = NULL;
@@ -3122,11 +3608,17 @@ iperf_reset_test(struct iperf_test *test)
test->settings->socket_bufsize = 0;
test->settings->blksize = DEFAULT_TCP_BLKSIZE;
test->settings->rate = 0;
+ test->settings->fqrate = 0;
test->settings->burst = 0;
test->settings->mss = 0;
test->settings->tos = 0;
+ /* Always initialize GSO/GRO fields */
+ test->settings->gso_dg_size = 0;
+ test->settings->gso_bf_size = GSO_BF_MAX_SIZE;
+ test->settings->gro_bf_size = GRO_BF_MAX_SIZE;
test->settings->dont_fragment = 0;
test->zerocopy = 0;
+ test->settings->skip_rx_copy = 0;
#if defined(HAVE_SSL)
if (test->settings->authtoken) {
@@ -3217,8 +3709,14 @@ iperf_stats_callback(struct iperf_test *test)
struct iperf_interval_results *irp, temp;
struct iperf_time temp_time;
iperf_size_t total_interval_bytes_transferred = 0;
+#if defined(HAVE_SCTP_H)
+ struct iperf_sctp_info sctp_info;
+#endif /* HAVE_SCTP_H */
temp.omitted = test->omitting;
+ temp.rtt = 0;
+ temp.rttvar = 0;
+ temp.pmtu = 0;
SLIST_FOREACH(sp, &test->streams, streams) {
rp = sp->result;
temp.bytes_transferred = sp->sender ? rp->bytes_sent_this_interval : rp->bytes_received_this_interval;
@@ -3269,6 +3767,8 @@ iperf_stats_callback(struct iperf_test *test)
temp.rttvar = get_rttvar(&temp);
temp.pmtu = get_pmtu(&temp);
+ temp.reorder = get_reorder(&temp);
+ rp->stream_reorder = temp.reorder;
}
}
} else {
@@ -3286,6 +3786,36 @@ iperf_stats_callback(struct iperf_test *test)
temp.outoforder_packets = sp->outoforder_packets;
temp.cnt_error = sp->cnt_error;
}
+
+#if defined(HAVE_SCTP_H)
+ if (test->protocol->id == Psctp) {
+ if (iperf_sctp_get_info(sp, &sctp_info) >= 0) {;
+ temp.pmtu = sctp_info.pmtu;
+ temp.rtt = sctp_info.rtt;
+ temp.snd_cwnd = sctp_info.cwnd;
+ temp.snd_wnd = sctp_info.wnd;
+ if (temp.snd_cwnd > rp->stream_max_snd_cwnd) {
+ rp->stream_max_snd_cwnd = temp.snd_cwnd;
+ }
+ if (temp.snd_wnd > rp->stream_max_snd_wnd) {
+ rp->stream_max_snd_wnd = temp.snd_wnd;
+ }
+ if (temp.rtt >= 0) {
+ temp.rtt = sctp_info.rtt;
+ if (temp.rtt > rp->stream_max_rtt) {
+ rp->stream_max_rtt = temp.rtt;
+ }
+ if (rp->stream_min_rtt == 0 ||
+ temp.rtt < rp->stream_min_rtt) {
+ rp->stream_min_rtt = temp.rtt;
+ }
+ rp->stream_sum_rtt += temp.rtt;
+ rp->stream_count_rtt++;
+ }
+ }
+ }
+#endif /* HAVE_SCTP_H */
+
add_to_interval_list(rp, &temp);
rp->bytes_sent_this_interval = rp->bytes_received_this_interval = 0;
}
@@ -3313,6 +3843,7 @@ iperf_print_intermediate(struct iperf_test *test)
int lower_mode, upper_mode;
int current_mode;
+ int discard_json;
/*
* Due to timing oddities, there can be cases, especially on the
@@ -3358,11 +3889,22 @@ iperf_print_intermediate(struct iperf_test *test)
return;
}
+ /*
+ * When we use streamed json, we don't actually need to keep the interval
+ * results around unless we're the server and the client requested the server output.
+ *
+ * This avoids unneeded memory build up for long sessions.
+ *
+ * The user can still opt in for all measurement data via the --json-stream-full-output option.
+ */
+ discard_json = test->json_stream == 1 && !test->json_stream_full_output && !(test->role == 's' && test->get_server_output);
+
if (test->json_output) {
json_interval = cJSON_CreateObject();
if (json_interval == NULL)
return;
- cJSON_AddItemToArray(test->json_intervals, json_interval);
+ if (!discard_json)
+ cJSON_AddItemToArray(test->json_intervals, json_interval);
json_interval_streams = cJSON_CreateArray();
if (json_interval_streams == NULL)
return;
@@ -3403,10 +3945,10 @@ iperf_print_intermediate(struct iperf_test *test)
iperf_size_t bytes = 0;
double bandwidth;
- int retransmits = 0;
+ int64_t retransmits = 0;
double start_time, end_time;
- int total_packets = 0, lost_packets = 0;
+ int64_t total_packets = 0, lost_packets = 0;
double avg_jitter = 0.0, lost_percent;
int stream_must_be_sender = current_mode * current_mode;
@@ -3415,7 +3957,7 @@ iperf_print_intermediate(struct iperf_test *test)
/* Print stream role just for bidirectional mode. */
if (test->mode == BIDIRECTIONAL) {
- sprintf(mbuf, "[%s-%s]", stream_must_be_sender?"TX":"RX", test->role == 'c'?"C":"S");
+ snprintf(mbuf, sizeof(mbuf), "[%s-%s]", stream_must_be_sender?"TX":"RX", test->role == 'c'?"C":"S");
} else {
mbuf[0] = '\0';
zbuf[0] = '\0';
@@ -3479,7 +4021,7 @@ iperf_print_intermediate(struct iperf_test *test)
if (test->sender_has_retransmits == 1 && stream_must_be_sender) {
/* Interval sum, TCP with retransmits. */
if (test->json_output)
- cJSON_AddItemToObject(json_interval, sum_name, iperf_json_printf("start: %f end: %f seconds: %f bytes: %d bits_per_second: %f retransmits: %d omitted: %b sender: %b", (double) start_time, (double) end_time, (double) irp->interval_duration, (int64_t) bytes, bandwidth * 8, (int64_t) retransmits, irp->omitted, stream_must_be_sender)); /* XXX irp->omitted or test->omitting? */
+ cJSON_AddItemToObject(json_interval, sum_name, iperf_json_printf("start: %f end: %f seconds: %f bytes: %d bits_per_second: %f retransmits: %d omitted: %b sender: %b", (double) start_time, (double) end_time, (double) irp->interval_duration, (int64_t) bytes, bandwidth * 8, retransmits, irp->omitted, stream_must_be_sender)); /* XXX irp->omitted or test->omitting? */
else
iperf_printf(test, report_sum_bw_retrans_format, mbuf, start_time, end_time, ubuf, nbuf, retransmits, irp->omitted?report_omitted:""); /* XXX irp->omitted or test->omitting? */
} else {
@@ -3513,6 +4055,11 @@ iperf_print_intermediate(struct iperf_test *test)
}
}
}
+
+ if (test->json_stream)
+ JSONStream_Output(test, "interval", json_interval);
+ if (discard_json)
+ cJSON_Delete(json_interval);
}
/**
@@ -3588,10 +4135,11 @@ iperf_print_results(struct iperf_test *test)
for (current_mode = lower_mode; current_mode <= upper_mode; ++current_mode) {
cJSON *json_summary_stream = NULL;
- int total_retransmits = 0;
- int total_packets = 0, lost_packets = 0;
- int sender_packet_count = 0, receiver_packet_count = 0; /* for this stream, this interval */
- int sender_total_packets = 0, receiver_total_packets = 0; /* running total */
+ int64_t total_retransmits = 0;
+ int64_t total_packets = 0, lost_packets = 0;
+ int64_t sender_packet_count = 0, receiver_packet_count = 0; /* for this stream, this interval */
+ int64_t sender_omitted_packet_count = 0, receiver_omitted_packet_count = 0; /* for this stream, this interval */
+ int64_t sender_total_packets = 0, receiver_total_packets = 0; /* running total */
char ubuf[UNIT_LEN];
char nbuf[UNIT_LEN];
struct stat sb;
@@ -3601,7 +4149,7 @@ iperf_print_results(struct iperf_test *test)
iperf_size_t bytes_received, total_received = 0;
double start_time, end_time = 0.0, avg_jitter = 0.0, lost_percent = 0.0;
double sender_time = 0.0, receiver_time = 0.0;
- struct iperf_time temp_time;
+ struct iperf_time temp_time;
double bandwidth;
char mbuf[UNIT_LEN];
@@ -3611,7 +4159,7 @@ iperf_print_results(struct iperf_test *test)
/* Print stream role just for bidirectional mode. */
if (test->mode == BIDIRECTIONAL) {
- sprintf(mbuf, "[%s-%s]", stream_must_be_sender?"TX":"RX", test->role == 'c'?"C":"S");
+ snprintf(mbuf, sizeof(mbuf), "[%s-%s]", stream_must_be_sender?"TX":"RX", test->role == 'c'?"C":"S");
} else {
mbuf[0] = '\0';
}
@@ -3639,8 +4187,8 @@ iperf_print_results(struct iperf_test *test)
*/
if (sp) {
- iperf_time_diff(&sp->result->start_time, &sp->result->end_time, &temp_time);
- end_time = iperf_time_in_secs(&temp_time);
+ iperf_time_diff(&sp->result->start_time, &sp->result->end_time, &temp_time);
+ end_time = iperf_time_in_secs(&temp_time);
if (sp->sender) {
sp->result->sender_time = end_time;
if (sp->result->receiver_time == 0.0) {
@@ -3671,11 +4219,15 @@ iperf_print_results(struct iperf_test *test)
if (sp->sender) {
sender_packet_count = sp->packet_count;
+ sender_omitted_packet_count = sp->omitted_packet_count;
receiver_packet_count = sp->peer_packet_count;
+ receiver_omitted_packet_count = sp->peer_omitted_packet_count;
}
else {
sender_packet_count = sp->peer_packet_count;
+ sender_omitted_packet_count = sp->peer_omitted_packet_count;
receiver_packet_count = sp->packet_count;
+ receiver_omitted_packet_count = sp->omitted_packet_count;
}
if (test->protocol->id == Ptcp || test->protocol->id == Psctp) {
@@ -3687,11 +4239,13 @@ iperf_print_results(struct iperf_test *test)
* Running total of the total number of packets. Use the sender packet count if we
* have it, otherwise use the receiver packet count.
*/
- int packet_count = sender_packet_count ? sender_packet_count : receiver_packet_count;
+ int64_t packet_count = sender_packet_count ? sender_packet_count : receiver_packet_count;
total_packets += (packet_count - sp->omitted_packet_count);
- sender_total_packets += (sender_packet_count - sp->omitted_packet_count);
- receiver_total_packets += (receiver_packet_count - sp->omitted_packet_count);
- lost_packets += (sp->cnt_error - sp->omitted_cnt_error);
+ sender_total_packets += (sender_packet_count - sender_omitted_packet_count);
+ receiver_total_packets += (receiver_packet_count - receiver_omitted_packet_count);
+ lost_packets += sp->cnt_error;
+ if (sp->omitted_cnt_error > -1)
+ lost_packets -= sp->omitted_cnt_error;
avg_jitter += sp->jitter;
}
@@ -3704,10 +4258,10 @@ iperf_print_results(struct iperf_test *test)
}
unit_snprintf(nbuf, UNIT_LEN, bandwidth, test->settings->unit_format);
if (test->protocol->id == Ptcp || test->protocol->id == Psctp) {
- if (test->sender_has_retransmits) {
+ if (test->sender_has_retransmits || test->protocol->id == Psctp) {
/* Sender summary, TCP and SCTP with retransmits. */
if (test->json_output)
- cJSON_AddItemToObject(json_summary_stream, "sender", iperf_json_printf("socket: %d start: %f end: %f seconds: %f bytes: %d bits_per_second: %f retransmits: %d max_snd_cwnd: %d max_snd_wnd: %d max_rtt: %d min_rtt: %d mean_rtt: %d sender: %b", (int64_t) sp->socket, (double) start_time, (double) sender_time, (double) sender_time, (int64_t) bytes_sent, bandwidth * 8, (int64_t) sp->result->stream_retrans, (int64_t) sp->result->stream_max_snd_cwnd, (int64_t) sp->result->stream_max_snd_wnd, (int64_t) sp->result->stream_max_rtt, (int64_t) sp->result->stream_min_rtt, (int64_t) ((sp->result->stream_count_rtt == 0) ? 0 : sp->result->stream_sum_rtt / sp->result->stream_count_rtt), stream_must_be_sender));
+ cJSON_AddItemToObject(json_summary_stream, report_sender, iperf_json_printf("socket: %d start: %f end: %f seconds: %f bytes: %d bits_per_second: %f retransmits: %d reorder: %d max_snd_cwnd: %d max_snd_wnd: %d max_rtt: %d min_rtt: %d mean_rtt: %d sender: %b", (int64_t) sp->socket, (double) start_time, (double) sender_time, (double) sender_time, (int64_t) bytes_sent, bandwidth * 8, (int64_t) sp->result->stream_retrans, (int64_t) sp->result->stream_reorder, (int64_t) sp->result->stream_max_snd_cwnd, (int64_t) sp->result->stream_max_snd_wnd, (int64_t) sp->result->stream_max_rtt, (int64_t) sp->result->stream_min_rtt, (int64_t) ((sp->result->stream_count_rtt == 0) ? 0 : sp->result->stream_sum_rtt / sp->result->stream_count_rtt), stream_must_be_sender));
else
if (test->role == 's' && !sp->sender) {
if (test->verbose)
@@ -3719,7 +4273,7 @@ iperf_print_results(struct iperf_test *test)
} else {
/* Sender summary, TCP and SCTP without retransmits. */
if (test->json_output)
- cJSON_AddItemToObject(json_summary_stream, "sender", iperf_json_printf("socket: %d start: %f end: %f seconds: %f bytes: %d bits_per_second: %f sender: %b", (int64_t) sp->socket, (double) start_time, (double) sender_time, (double) sender_time, (int64_t) bytes_sent, bandwidth * 8, stream_must_be_sender));
+ cJSON_AddItemToObject(json_summary_stream, report_sender, iperf_json_printf("socket: %d start: %f end: %f seconds: %f bytes: %d bits_per_second: %f sender: %b", (int64_t) sp->socket, (double) start_time, (double) sender_time, (double) sender_time, (int64_t) bytes_sent, bandwidth * 8, stream_must_be_sender));
else
if (test->role == 's' && !sp->sender) {
if (test->verbose)
@@ -3731,15 +4285,15 @@ iperf_print_results(struct iperf_test *test)
}
} else {
/* Sender summary, UDP. */
- if (sender_packet_count - sp->omitted_packet_count > 0) {
- lost_percent = 100.0 * (sp->cnt_error - sp->omitted_cnt_error) / (sender_packet_count - sp->omitted_packet_count);
+ if (sender_packet_count - sender_omitted_packet_count > 0) {
+ lost_percent = 100.0 * (sp->cnt_error - sp->omitted_cnt_error) / (sender_packet_count - sender_omitted_packet_count);
}
else {
lost_percent = 0.0;
}
if (test->json_output) {
/*
- * For hysterical raisins, we only emit one JSON
+ * For historical reasons, we only emit one JSON
* object for the UDP summary, and it contains
* information for both the sender and receiver
* side.
@@ -3754,7 +4308,7 @@ iperf_print_results(struct iperf_test *test)
* is the case, then use the receiver's count of packets
* instead.
*/
- int packet_count = sender_packet_count ? sender_packet_count : receiver_packet_count;
+ int64_t packet_count = sender_packet_count ? sender_packet_count : receiver_packet_count;
cJSON_AddItemToObject(json_summary_stream, "udp", iperf_json_printf("socket: %d start: %f end: %f seconds: %f bytes: %d bits_per_second: %f jitter_ms: %f lost_packets: %d packets: %d lost_percent: %f out_of_order: %d sender: %b", (int64_t) sp->socket, (double) start_time, (double) sender_time, (double) sender_time, (int64_t) bytes_sent, bandwidth * 8, (double) sp->jitter * 1000.0, (int64_t) (sp->cnt_error - sp->omitted_cnt_error), (int64_t) (packet_count - sp->omitted_packet_count), (double) lost_percent, (int64_t) (sp->outoforder_packets - sp->omitted_outoforder_packets), stream_must_be_sender));
}
else {
@@ -3770,7 +4324,7 @@ iperf_print_results(struct iperf_test *test)
iperf_printf(test, report_sender_not_available_format, sp->socket);
}
else {
- iperf_printf(test, report_bw_udp_format, sp->socket, mbuf, start_time, sender_time, ubuf, nbuf, 0.0, 0, (sender_packet_count - sp->omitted_packet_count), (double) 0, report_sender);
+ iperf_printf(test, report_bw_udp_format, sp->socket, mbuf, start_time, sender_time, ubuf, nbuf, 0.0, (int64_t) 0, (sender_packet_count - sender_omitted_packet_count), (double) 0, report_sender);
}
if ((sp->outoforder_packets - sp->omitted_outoforder_packets) > 0)
iperf_printf(test, report_sum_outoforder, mbuf, start_time, sender_time, (sp->outoforder_packets - sp->omitted_outoforder_packets));
@@ -3810,7 +4364,7 @@ iperf_print_results(struct iperf_test *test)
if (test->protocol->id == Ptcp || test->protocol->id == Psctp) {
/* Receiver summary, TCP and SCTP */
if (test->json_output)
- cJSON_AddItemToObject(json_summary_stream, "receiver", iperf_json_printf("socket: %d start: %f end: %f seconds: %f bytes: %d bits_per_second: %f sender: %b", (int64_t) sp->socket, (double) start_time, (double) receiver_time, (double) end_time, (int64_t) bytes_received, bandwidth * 8, stream_must_be_sender));
+ cJSON_AddItemToObject(json_summary_stream, report_receiver, iperf_json_printf("socket: %d start: %f end: %f seconds: %f bytes: %d bits_per_second: %f sender: %b", (int64_t) sp->socket, (double) start_time, (double) receiver_time, (double) end_time, (int64_t) bytes_received, bandwidth * 8, stream_must_be_sender));
else
if (test->role == 's' && sp->sender) {
if (test->verbose)
@@ -3827,8 +4381,10 @@ iperf_print_results(struct iperf_test *test)
* data here.
*/
if (! test->json_output) {
- if (receiver_packet_count - sp->omitted_packet_count > 0) {
- lost_percent = 100.0 * (sp->cnt_error - sp->omitted_cnt_error) / (receiver_packet_count - sp->omitted_packet_count);
+ if (test->omit == 0 && receiver_packet_count > 0) {
+ lost_percent = 100.0 * sp->cnt_error / receiver_packet_count;
+ } else if (receiver_packet_count - receiver_omitted_packet_count > 0 && sp->omitted_cnt_error > -1) {
+ lost_percent = 100.0 * (sp->cnt_error - sp->omitted_cnt_error) / (receiver_packet_count - receiver_omitted_packet_count);
}
else {
lost_percent = 0.0;
@@ -3839,7 +4395,13 @@ iperf_print_results(struct iperf_test *test)
iperf_printf(test, report_receiver_not_available_format, sp->socket);
}
else {
- iperf_printf(test, report_bw_udp_format, sp->socket, mbuf, start_time, receiver_time, ubuf, nbuf, sp->jitter * 1000.0, (sp->cnt_error - sp->omitted_cnt_error), (receiver_packet_count - sp->omitted_packet_count), lost_percent, report_receiver);
+ if (test->omit == 0) {
+ iperf_printf(test, report_bw_udp_format, sp->socket, mbuf, start_time, receiver_time, ubuf, nbuf, sp->jitter * 1000.0, sp->cnt_error, receiver_packet_count, lost_percent, report_receiver);
+ } else if (sp->omitted_cnt_error > -1) {
+ iperf_printf(test, report_bw_udp_format, sp->socket, mbuf, start_time, receiver_time, ubuf, nbuf, sp->jitter * 1000.0, (sp->cnt_error - sp->omitted_cnt_error), (receiver_packet_count - receiver_omitted_packet_count), lost_percent, report_receiver);
+ } else {
+ iperf_printf(test, report_bw_udp_format_no_omitted_error, sp->socket, mbuf, start_time, receiver_time, ubuf, nbuf, sp->jitter * 1000.0, (receiver_packet_count - receiver_omitted_packet_count), report_receiver);
+ }
}
}
}
@@ -3940,13 +4502,16 @@ iperf_print_results(struct iperf_test *test)
* ambiguities between the sender and receiver.
*/
cJSON_AddItemToObject(test->json_end, sum_name, iperf_json_printf("start: %f end: %f seconds: %f bytes: %d bits_per_second: %f jitter_ms: %f lost_packets: %d packets: %d lost_percent: %f sender: %b", (double) start_time, (double) receiver_time, (double) receiver_time, (int64_t) total_sent, bandwidth * 8, (double) avg_jitter * 1000.0, (int64_t) lost_packets, (int64_t) total_packets, (double) lost_percent, stream_must_be_sender));
+
+ double sent_bandwidth = sender_time > 0 ? ((double) total_sent * 8 / sender_time) : 0.0;
+ double recv_bandwidth = receiver_time > 0 ? ((double) total_received * 8 / receiver_time) : 0.0;
/*
* Separate sum_sent and sum_received structures.
* Using these structures to get the most complete
* information about UDP transfer.
*/
- cJSON_AddItemToObject(test->json_end, sum_sent_name, iperf_json_printf("start: %f end: %f seconds: %f bytes: %d bits_per_second: %f jitter_ms: %f lost_packets: %d packets: %d lost_percent: %f sender: %b", (double) start_time, (double) sender_time, (double) sender_time, (int64_t) total_sent, (double) total_sent * 8 / sender_time, (double) 0.0, (int64_t) 0, (int64_t) sender_total_packets, (double) 0.0, 1));
- cJSON_AddItemToObject(test->json_end, sum_received_name, iperf_json_printf("start: %f end: %f seconds: %f bytes: %d bits_per_second: %f jitter_ms: %f lost_packets: %d packets: %d lost_percent: %f sender: %b", (double) start_time, (double) receiver_time, (double) receiver_time, (int64_t) total_received, (double) total_received * 8 / receiver_time, (double) avg_jitter * 1000.0, (int64_t) lost_packets, (int64_t) receiver_total_packets, (double) lost_percent, 0));
+ cJSON_AddItemToObject(test->json_end, sum_sent_name, iperf_json_printf("start: %f end: %f seconds: %f bytes: %d bits_per_second: %f jitter_ms: %f lost_packets: %d packets: %d lost_percent: %f sender: %b", (double) start_time, (double) sender_time, (double) sender_time, (int64_t) total_sent, sent_bandwidth, (double) 0.0, (int64_t) 0, (int64_t) sender_total_packets, (double) 0.0, 1));
+ cJSON_AddItemToObject(test->json_end, sum_received_name, iperf_json_printf("start: %f end: %f seconds: %f bytes: %d bits_per_second: %f jitter_ms: %f lost_packets: %d packets: %d lost_percent: %f sender: %b", (double) start_time, (double) receiver_time, (double) receiver_time, (int64_t) total_received, recv_bandwidth, (double) avg_jitter * 1000.0, (int64_t) lost_packets, (int64_t) receiver_total_packets, (double) lost_percent, 0));
} else {
/*
* On the client we have both sender and receiver overall summary
@@ -3955,7 +4520,7 @@ iperf_print_results(struct iperf_test *test)
*/
if (! (test->role == 's' && !stream_must_be_sender) ) {
unit_snprintf(ubuf, UNIT_LEN, (double) total_sent, 'A');
- iperf_printf(test, report_sum_bw_udp_format, mbuf, start_time, sender_time, ubuf, nbuf, 0.0, 0, sender_total_packets, 0.0, "sender");
+ iperf_printf(test, report_sum_bw_udp_format, mbuf, start_time, sender_time, ubuf, nbuf, 0.0, (int64_t) 0, sender_total_packets, 0.0, report_sender);
}
if (! (test->role == 's' && stream_must_be_sender) ) {
@@ -3968,7 +4533,7 @@ iperf_print_results(struct iperf_test *test)
bandwidth = 0.0;
}
unit_snprintf(nbuf, UNIT_LEN, bandwidth, test->settings->unit_format);
- iperf_printf(test, report_sum_bw_udp_format, mbuf, start_time, receiver_time, ubuf, nbuf, avg_jitter * 1000.0, lost_packets, receiver_total_packets, lost_percent, "receiver");
+ iperf_printf(test, report_sum_bw_udp_format, mbuf, start_time, receiver_time, ubuf, nbuf, avg_jitter * 1000.0, lost_packets, receiver_total_packets, lost_percent, report_receiver);
}
}
}
@@ -4033,6 +4598,7 @@ iperf_print_results(struct iperf_test *test)
}
if (test->server_output_text) {
iperf_printf(test, "\nServer output:\n%s\n", test->server_output_text);
+ free(test->server_output_text);
test->server_output_text = NULL;
}
}
@@ -4089,7 +4655,7 @@ print_interval_results(struct iperf_test *test, struct iperf_stream *sp, cJSON *
double bandwidth, lost_percent;
if (test->mode == BIDIRECTIONAL) {
- sprintf(mbuf, "[%s-%s]", sp->sender?"TX":"RX", test->role == 'c'?"C":"S");
+ snprintf(mbuf, sizeof(mbuf), "[%s-%s]", sp->sender?"TX":"RX", test->role == 'c'?"C":"S");
} else {
mbuf[0] = '\0';
zbuf[0] = '\0';
@@ -4151,10 +4717,10 @@ print_interval_results(struct iperf_test *test, struct iperf_stream *sp, cJSON *
et = iperf_time_in_secs(&temp_time);
if (test->protocol->id == Ptcp || test->protocol->id == Psctp) {
- if (test->sender_has_retransmits == 1 && sp->sender) {
+ if ((test->sender_has_retransmits == 1 || test->protocol->id == Psctp) && sp->sender) {
/* Interval, TCP with retransmits. */
if (test->json_output)
- cJSON_AddItemToArray(json_interval_streams, iperf_json_printf("socket: %d start: %f end: %f seconds: %f bytes: %d bits_per_second: %f retransmits: %d snd_cwnd: %d snd_wnd: %d rtt: %d rttvar: %d pmtu: %d omitted: %b sender: %b", (int64_t) sp->socket, (double) st, (double) et, (double) irp->interval_duration, (int64_t) irp->bytes_transferred, bandwidth * 8, (int64_t) irp->interval_retrans, (int64_t) irp->snd_cwnd, (int64_t) irp->snd_wnd, (int64_t) irp->rtt, (int64_t) irp->rttvar, (int64_t) irp->pmtu, irp->omitted, sp->sender));
+ cJSON_AddItemToArray(json_interval_streams, iperf_json_printf("socket: %d start: %f end: %f seconds: %f bytes: %d bits_per_second: %f retransmits: %d snd_cwnd: %d snd_wnd: %d rtt: %d rttvar: %d pmtu: %d reorder: %d omitted: %b sender: %b", (int64_t) sp->socket, (double) st, (double) et, (double) irp->interval_duration, (int64_t) irp->bytes_transferred, bandwidth * 8, (int64_t) irp->interval_retrans, (int64_t) irp->snd_cwnd, (int64_t) irp->snd_wnd, (int64_t) irp->rtt, (int64_t) irp->rttvar, (int64_t) irp->pmtu, (int64_t) irp->reorder, irp->omitted, sp->sender));
else {
unit_snprintf(cbuf, UNIT_LEN, irp->snd_cwnd, 'A');
iperf_printf(test, report_bw_retrans_cwnd_format, sp->socket, mbuf, st, et, ubuf, nbuf, irp->interval_retrans, cbuf, irp->omitted?report_omitted:"");
@@ -4218,6 +4784,7 @@ iperf_new_stream(struct iperf_test *test, int s, int sender)
{
struct iperf_stream *sp;
int ret = 0;
+ int size;
char template[1024];
if (test->tmp_template) {
@@ -4276,13 +4843,20 @@ iperf_new_stream(struct iperf_test *test, int s, int sender)
free(sp);
return NULL;
}
- if (ftruncate(sp->buffer_fd, test->settings->blksize) < 0) {
+ size = test->settings->blksize;
+ if (test->protocol->id == Pudp && test->settings->gso && (size < test->settings->gso_bf_size))
+ size = test->settings->gso_bf_size;
+ if (test->protocol->id == Pudp && test->settings->gro && (size < test->settings->gro_bf_size))
+ size = test->settings->gro_bf_size;
+ if (sp->test->debug)
+ printf("Buffer %d bytes\n", size);
+ if (ftruncate(sp->buffer_fd, size) < 0) {
i_errno = IECREATESTREAM;
free(sp->result);
free(sp);
return NULL;
}
- sp->buffer = (char *) mmap(NULL, test->settings->blksize, PROT_READ|PROT_WRITE, MAP_PRIVATE, sp->buffer_fd, 0);
+ sp->buffer = (char *) mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, sp->buffer_fd, 0);
if (sp->buffer == MAP_FAILED) {
i_errno = IECREATESTREAM;
free(sp->result);
@@ -4445,12 +5019,15 @@ iperf_add_stream(struct iperf_test *test, struct iperf_stream *sp)
// and changing it would break multi-stream tests between old
// and new iperf3 versions.
i = 2;
+ prev = NULL;
SLIST_FOREACH(n, &test->streams, streams) {
prev = n;
++i;
}
- SLIST_INSERT_AFTER(prev, sp, streams);
- sp->id = i;
+ if (prev) {
+ SLIST_INSERT_AFTER(prev, sp, streams);
+ sp->id = i;
+ }
}
}
@@ -4564,8 +5141,10 @@ iperf_catch_sigend(void (*handler)(int))
* before cleaning up and exiting.
*/
void
-iperf_got_sigend(struct iperf_test *test)
+iperf_got_sigend(struct iperf_test *test, int sig)
{
+ int exit_normal;
+
/*
* If we're the client, or if we're a server and running a test,
* then dump out the accumulated stats so far.
@@ -4576,18 +5155,36 @@ iperf_got_sigend(struct iperf_test *test)
test->done = 1;
cpu_util(test->cpu_util);
test->stats_callback(test);
- test->state = DISPLAY_RESULTS; /* change local state only */
+ iperf_set_test_state(test, DISPLAY_RESULTS); /* change local state only */
if (test->on_test_finish)
test->on_test_finish(test);
test->reporter_callback(test);
}
if (test->ctrl_sck >= 0) {
- test->state = (test->role == 'c') ? CLIENT_TERMINATE : SERVER_TERMINATE;
+ iperf_set_test_state(test, (test->role == 'c') ? CLIENT_TERMINATE : SERVER_TERMINATE);
(void) Nwrite(test->ctrl_sck, (char*) &test->state, sizeof(signed char), Ptcp);
}
i_errno = (test->role == 'c') ? IECLIENTTERM : IESERVERTERM;
- iperf_errexit(test, "interrupt - %s", iperf_strerror(i_errno));
+
+ exit_normal = 0;
+#ifdef SIGTERM
+ if (sig == SIGTERM)
+ exit_normal = 1;
+#endif
+#ifdef SIGINT
+ if (sig == SIGINT)
+ exit_normal = 1;
+#endif
+#ifdef SIGHUP
+ if (sig == SIGHUP)
+ exit_normal = 1;
+#endif
+ if (exit_normal) {
+ iperf_signormalexit(test, "interrupt - %s by signal %s(%d)", iperf_strerror(i_errno), strsignal(sig), sig);
+ } else {
+ iperf_errexit(test, "interrupt - %s by signal %s(%d)", iperf_strerror(i_errno), strsignal(sig), sig);
+ }
}
/* Try to write a PID file if requested, return -1 on an error. */
@@ -4609,7 +5206,15 @@ iperf_create_pidfile(struct iperf_test *test)
if (pid > 0) {
/* See if the process exists. */
+#if (defined(__vxworks)) || (defined(__VXWORKS__))
+#if (defined(_WRS_KERNEL)) && (defined(_WRS_CONFIG_LP64))
+ if (kill((_Vx_TASK_ID)pid, 0) == 0) {
+#else
+ if (kill(pid, 0) == 0) {
+#endif // _WRS_KERNEL and _WRS_CONFIG_LP64
+#else
if (kill(pid, 0) == 0) {
+#endif // __vxworks or __VXWORKS__
/*
* Make sure not to try to delete existing PID file by
* scribbling over the pathname we'd use to refer to it.
@@ -4621,6 +5226,7 @@ iperf_create_pidfile(struct iperf_test *test)
}
}
}
+ (void)close(fd);
}
/*
@@ -4683,30 +5289,73 @@ iperf_json_start(struct iperf_test *test)
int
iperf_json_finish(struct iperf_test *test)
{
- if (test->title)
- cJSON_AddStringToObject(test->json_top, "title", test->title);
- if (test->extra_data)
- cJSON_AddStringToObject(test->json_top, "extra_data", test->extra_data);
- /* Include server output */
- if (test->json_server_output) {
- cJSON_AddItemToObject(test->json_top, "server_output_json", test->json_server_output);
- }
- if (test->server_output_text) {
- cJSON_AddStringToObject(test->json_top, "server_output_text", test->server_output_text);
+ if (test->json_top) {
+ if (test->title) {
+ cJSON_AddStringToObject(test->json_top, "title", test->title);
+ }
+ if (test->extra_data) {
+ cJSON_AddStringToObject(test->json_top, "extra_data", test->extra_data);
+ }
+ /* Include server output */
+ if (test->json_server_output) {
+ cJSON_AddItemToObject(test->json_top, "server_output_json", test->json_server_output);
+ }
+ if (test->server_output_text) {
+ cJSON_AddStringToObject(test->json_top, "server_output_text", test->server_output_text);
+ }
+
+ int print_full_json = 1;
+
+ /* --json-stream, so we print various individual objects */
+ if (test->json_stream) {
+ cJSON *error = iperf_cJSON_GetObjectItemType(test->json_top, "error", cJSON_String);
+ if (error) {
+ JSONStream_Output(test, "error", error);
+ }
+ if (test->json_server_output) {
+ JSONStream_Output(test, "server_output_json", test->json_server_output);
+ }
+ if (test->server_output_text) {
+ JSONStream_Output(test, "server_output_text", cJSON_CreateString(test->server_output_text));
+ }
+ JSONStream_Output(test, "end", test->json_end);
+
+ if (!test->json_stream_full_output)
+ print_full_json = 0;
+ }
+ /* Original --json output, single monolithic object */
+ if (print_full_json) {
+ /*
+ * Get ASCII rendering of JSON structure. Then make our
+ * own copy of it and return the storage that cJSON
+ * allocated on our behalf. We keep our own copy
+ * around.
+ */
+ char *str = cJSON_Print(test->json_top);
+ if (str == NULL) {
+ return -1;
+ }
+ test->json_output_string = strdup(str);
+ cJSON_free(str);
+ if (test->json_output_string == NULL) {
+ return -1;
+ }
+ if (test->json_callback != NULL) {
+ (test->json_callback)(test, test->json_output_string);
+ } else {
+ if (pthread_mutex_lock(&(test->print_mutex)) != 0) {
+ perror("iperf_json_finish: pthread_mutex_lock");
+ }
+ fprintf(test->outfile, "%s\n", test->json_output_string);
+ if (pthread_mutex_unlock(&(test->print_mutex)) != 0) {
+ perror("iperf_json_finish: pthread_mutex_unlock");
+ }
+ iflush(test);
+ }
+ }
+ cJSON_Delete(test->json_top);
}
- // Get ASCII rendering of JSON structure. Then make our
- // own copy of it and return the storage that cJSON allocated
- // on our behalf. We keep our own copy around.
- char *str = cJSON_Print(test->json_top);
- if (str == NULL)
- return -1;
- test->json_output_string = strdup(str);
- cJSON_free(str);
- if (test->json_output_string == NULL)
- return -1;
- fprintf(test->outfile, "%s\n", test->json_output_string);
- iflush(test);
- cJSON_Delete(test->json_top);
+
test->json_top = test->json_start = test->json_connected = test->json_intervals = test->json_server_output = test->json_end = NULL;
return 0;
}
@@ -4811,6 +5460,10 @@ iperf_printf(struct iperf_test *test, const char* format, ...)
struct tm *ltm = NULL;
char *ct = NULL;
+ if (pthread_mutex_lock(&(test->print_mutex)) != 0) {
+ perror("iperf_print: pthread_mutex_lock");
+ }
+
/* Timestamp if requested */
if (iperf_get_test_timestamps(test)) {
time(&now);
@@ -4834,28 +5487,36 @@ iperf_printf(struct iperf_test *test, const char* format, ...)
if (test->role == 'c') {
if (ct) {
r0 = fprintf(test->outfile, "%s", ct);
- if (r0 < 0)
- return r0;
+ if (r0 < 0) {
+ r = r0;
+ goto bottom;
+ }
r += r0;
}
if (test->title) {
r0 = fprintf(test->outfile, "%s: ", test->title);
- if (r0 < 0)
- return r0;
+ if (r0 < 0) {
+ r = r0;
+ goto bottom;
+ }
r += r0;
}
va_start(argp, format);
r0 = vfprintf(test->outfile, format, argp);
va_end(argp);
- if (r0 < 0)
- return r0;
+ if (r0 < 0) {
+ r = r0;
+ goto bottom;
+ }
r += r0;
}
else if (test->role == 's') {
if (ct) {
r0 = snprintf(linebuffer, sizeof(linebuffer), "%s", ct);
- if (r0 < 0)
- return r0;
+ if (r0 < 0) {
+ r = r0;
+ goto bottom;
+ }
r += r0;
}
/* Should always be true as long as sizeof(ct) < sizeof(linebuffer) */
@@ -4863,8 +5524,10 @@ iperf_printf(struct iperf_test *test, const char* format, ...)
va_start(argp, format);
r0 = vsnprintf(linebuffer + r, sizeof(linebuffer) - r, format, argp);
va_end(argp);
- if (r0 < 0)
- return r0;
+ if (r0 < 0) {
+ r = r0;
+ goto bottom;
+ }
r += r0;
}
fprintf(test->outfile, "%s", linebuffer);
@@ -4875,11 +5538,113 @@ iperf_printf(struct iperf_test *test, const char* format, ...)
TAILQ_INSERT_TAIL(&(test->server_output_list), l, textlineentries);
}
}
+
+ bottom:
+ if (pthread_mutex_unlock(&(test->print_mutex)) != 0) {
+ perror("iperf_print: pthread_mutex_unlock");
+ }
+
return r;
}
int
iflush(struct iperf_test *test)
{
- return fflush(test->outfile);
+ int rc2;
+
+ int rc;
+ rc = pthread_mutex_lock(&(test->print_mutex));
+ if (rc != 0) {
+ errno = rc;
+ perror("iflush: pthread_mutex_lock");
+ }
+
+ rc2 = fflush(test->outfile);
+
+ rc = pthread_mutex_unlock(&(test->print_mutex));
+ if (rc != 0) {
+ errno = rc;
+ perror("iflush: pthread_mutex_unlock");
+ }
+
+ return rc2;
+}
+
+#if defined (HAVE_TCP_KEEPALIVE)
+// Set Control Connection TCP Keepalive (especially useful for long UDP test sessions)
+int
+iperf_set_control_keepalive(struct iperf_test *test)
+{
+ int opt, kaidle, kainterval, kacount;
+ socklen_t len;
+
+ if (test->settings->cntl_ka) {
+ // Set keepalive using system defaults
+ opt = 1;
+ if (setsockopt(test->ctrl_sck, SOL_SOCKET, SO_KEEPALIVE, (char *) &opt, sizeof(opt))) {
+ i_errno = IESETCNTLKA;
+ return -1;
+ }
+
+ // Get default values when not specified
+ if ((kaidle = test->settings->cntl_ka_keepidle) == 0) {
+ len = sizeof(kaidle);
+ if (getsockopt(test->ctrl_sck, IPPROTO_TCP, TCP_KEEPIDLE, (char *) &kaidle, &len)) {
+ i_errno = IESETCNTLKAINTERVAL;
+ return -1;
+ }
+ }
+ if ((kainterval = test->settings->cntl_ka_interval) == 0) {
+ len = sizeof(kainterval);
+ if (getsockopt(test->ctrl_sck, IPPROTO_TCP, TCP_KEEPINTVL, (char *) &kainterval, &len)) {
+ i_errno = IESETCNTLKAINTERVAL;
+ return -1;
+ }
+ }
+ if ((kacount = test->settings->cntl_ka_count) == 0) {
+ len = sizeof(kacount);
+ if (getsockopt(test->ctrl_sck, IPPROTO_TCP, TCP_KEEPCNT, (char *) &kacount, &len)) {
+ i_errno = IESETCNTLKACOUNT;
+ return -1;
+ }
+ }
+
+ // Seems that at least in Windows WSL2, TCP keepalive retries full interval must be
+ // smaller than the idle interval. Otherwise, the keepalive message is sent only once.
+ if (test->settings->cntl_ka_keepidle) {
+ if (test->settings->cntl_ka_keepidle <= (kainterval * kacount)) {
+ iperf_err(test, "Keepalive Idle time (%d) should be greater than Retries-interval (%d) times Retries-count (%d)", kaidle, kainterval, kacount);
+ i_errno = IECNTLKA;
+ return -1;
+ }
+ }
+
+ // Set keep alive values when specified
+ if ((opt = test->settings->cntl_ka_keepidle)) {
+ if (setsockopt(test->ctrl_sck, IPPROTO_TCP, TCP_KEEPIDLE, (char *) &opt, sizeof(opt))) {
+ i_errno = IESETCNTLKAKEEPIDLE;
+ return -1;
+ }
+ }
+ if ((opt = test->settings->cntl_ka_interval)) {
+ if (setsockopt(test->ctrl_sck, IPPROTO_TCP, TCP_KEEPINTVL, (char *) &opt, sizeof(opt))) {
+ i_errno = IESETCNTLKAINTERVAL;
+ return -1;
+ }
+ }
+ if ((opt = test->settings->cntl_ka_count)) {
+ if (setsockopt(test->ctrl_sck, IPPROTO_TCP, TCP_KEEPCNT, (char *) &opt, sizeof(opt))) {
+ i_errno = IESETCNTLKACOUNT;
+ return -1;
+ }
+ }
+
+ if (test->verbose) {
+ printf("Control connection TCP Keepalive TCP_KEEPIDLE/TCP_KEEPINTVL/TCP_KEEPCNT are set to %d/%d/%d\n",
+ kaidle, kainterval, kacount);
+ }
+ }
+
+ return 0;
}
+#endif //HAVE_TCP_KEEPALIVE
diff --git a/src/iperf_api.h b/src/iperf_api.h
index 171006aeb..25ac951e0 100644
--- a/src/iperf_api.h
+++ b/src/iperf_api.h
@@ -1,5 +1,5 @@
/*
- * iperf, Copyright (c) 2014-2022, The Regents of the University of
+ * iperf, Copyright (c) 2014-2025, 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.
@@ -30,14 +30,23 @@
#include
#include
#include
+#include
#include
-#ifdef HAVE_STDINT_H
#include
-#endif
#ifdef __cplusplus
extern "C" { /* open extern "C" */
#endif
+/*
+ * Atomic types highly desired, but if not, we approximate what we need
+ * with normal integers and warn.
+ */
+#ifdef HAVE_STDATOMIC_H
+#include
+#else
+#warning "No available"
+typedef u_int64_t atomic_uint_fast64_t;
+#endif // HAVE_STDATOMIC_H
struct iperf_test;
struct iperf_stream_result;
@@ -46,7 +55,8 @@ struct iperf_stream;
struct iperf_time;
#if !defined(__IPERF_H)
-typedef uint64_t iperf_size_t;
+typedef uint_fast64_t iperf_size_t;
+typedef atomic_uint_fast64_t atomic_iperf_size_t;
#endif // __IPERF_H
/* default settings */
@@ -89,7 +99,14 @@ typedef uint64_t iperf_size_t;
#define OPT_IDLE_TIMEOUT 25
#define OPT_DONT_FRAGMENT 26
#define OPT_RCV_TIMEOUT 27
-#define OPT_SND_TIMEOUT 28
+#define OPT_JSON_STREAM 28
+#define OPT_SND_TIMEOUT 29
+#define OPT_USE_PKCS1_PADDING 30
+#define OPT_CNTL_KA 31
+#define OPT_SKIP_RX_COPY 32
+#define OPT_JSON_STREAM_FULL_OUTPUT 33
+#define OPT_SERVER_MAX_DURATION 34
+#define OPT_GSRO 35
/* states */
#define TEST_START 1
@@ -140,6 +157,8 @@ char* iperf_get_test_template( struct iperf_test* ipt );
int iperf_get_test_protocol_id( struct iperf_test* ipt );
int iperf_get_test_json_output( struct iperf_test* ipt );
char* iperf_get_test_json_output_string ( struct iperf_test* ipt );
+int iperf_get_test_json_stream( struct iperf_test* ipt );
+int iperf_get_test_json_stream_full_output( struct iperf_test* ipt );
int iperf_get_test_zerocopy( struct iperf_test* ipt );
int iperf_get_test_get_server_output( struct iperf_test* ipt );
char iperf_get_test_unit_format(struct iperf_test *ipt);
@@ -184,6 +203,9 @@ void iperf_set_test_server_hostname( struct iperf_test* ipt, const char* server_
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 );
+void iperf_set_test_json_stream( struct iperf_test* ipt, int json_stream );
+void iperf_set_test_json_stream_full_output( struct iperf_test* ipt, int json_stream_full_output );
+void iperf_set_test_json_callback(struct iperf_test *ipt, void (*callback)(struct iperf_test *, char *));
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 );
@@ -200,14 +222,20 @@ void iperf_set_dont_fragment( struct iperf_test* ipt, int dont_fragment );
void iperf_set_test_congestion_control(struct iperf_test* ipt, char* cc);
void iperf_set_test_mss(struct iperf_test* ipt, int mss);
void iperf_set_mapped_v4(struct iperf_test* ipt, const int val);
+void iperf_set_on_new_stream_callback(struct iperf_test* ipt, void (*callback)(struct iperf_stream *));
+void iperf_set_on_test_start_callback(struct iperf_test* ipt, void (*callback)(struct iperf_test *));
+void iperf_set_on_test_connect_callback(struct iperf_test* ipt, void (*callback)(struct iperf_test *));
+void iperf_set_on_test_finish_callback(struct iperf_test* ipt, void (*callback)(struct iperf_test *));
#if defined(HAVE_SSL)
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_client_rsa_pubkey_from_file(struct iperf_test *ipt, const char *client_rsa_pubkey_file);
void iperf_set_test_server_authorized_users(struct iperf_test *ipt, const char *server_authorized_users);
void iperf_set_test_server_skew_threshold(struct iperf_test *ipt, int server_skew_threshold);
void iperf_set_test_server_rsa_privkey(struct iperf_test *ipt, const char *server_rsa_privkey_base64);
+void iperf_set_test_server_rsa_privkey_from_file(struct iperf_test *ipt, const char *server_rsa_privkey_file);
#endif // HAVE_SSL
void iperf_set_test_connect_timeout(struct iperf_test *ipt, int ct);
@@ -226,7 +254,7 @@ void add_to_interval_list(struct iperf_stream_result * rp, struct iperf_int
/**
* connect_msg -- displays connection message
- * denoting senfer/receiver details
+ * denoting sender/receiver details
*
*/
void connect_msg(struct iperf_stream * sp);
@@ -292,6 +320,14 @@ void iperf_free_stream(struct iperf_stream * sp);
*/
int iperf_common_sockopts(struct iperf_test *, int s);
+#if defined (HAVE_TCP_KEEPALIVE)
+/**
+ * iperf_set_control_keepalive -- set control connection TCP keepalive
+ *
+ */
+int iperf_set_control_keepalive(struct iperf_test *test);
+#endif //HAVE_TCP_KEEPALIVE
+
int has_tcpinfo(void);
int has_tcpinfo_retransmits(void);
void save_tcpinfo(struct iperf_stream *sp, struct iperf_interval_results *irp);
@@ -301,15 +337,15 @@ long get_snd_wnd(struct iperf_interval_results *irp);
long get_rtt(struct iperf_interval_results *irp);
long get_rttvar(struct iperf_interval_results *irp);
long get_pmtu(struct iperf_interval_results *irp);
+long get_reorder(struct iperf_interval_results *irp);
void print_tcpinfo(struct iperf_test *test);
-void build_tcpinfo_message(struct iperf_interval_results *r, char *message);
int iperf_set_send_state(struct iperf_test *test, signed char state);
void iperf_check_throttle(struct iperf_stream *sp, struct iperf_time *nowP);
-int iperf_send(struct iperf_test *, fd_set *) /* __attribute__((hot)) */;
-int iperf_recv(struct iperf_test *, fd_set *);
+int iperf_send_mt(struct iperf_stream *) /* __attribute__((hot)) */;
+int iperf_recv_mt(struct iperf_stream *);
void iperf_catch_sigend(void (*handler)(int));
-void iperf_got_sigend(struct iperf_test *test) __attribute__ ((noreturn));
+void iperf_got_sigend(struct iperf_test *test, int sig) __attribute__ ((noreturn));
void usage(void);
void usage_long(FILE * f);
void warning(const char *);
@@ -363,8 +399,11 @@ int iflush(struct iperf_test *test);
/* Error routines. */
void iperf_err(struct iperf_test *test, const char *format, ...) __attribute__ ((format(printf,2,3)));
void iperf_errexit(struct iperf_test *test, const char *format, ...) __attribute__ ((format(printf,2,3),noreturn));
+void iperf_signormalexit(struct iperf_test *test, const char *format, ...) __attribute__ ((format(printf,2,3),noreturn));
+void iperf_exit(struct iperf_test *test, int exit_code, const char *format, va_list argp) __attribute__ ((noreturn));
char *iperf_strerror(int);
extern int i_errno;
+extern const char *errarg;
enum {
IENONE = 0, // No error
/* Parameter errors */
@@ -402,6 +441,10 @@ enum {
IERVRSONLYRCVTIMEOUT = 32, // Client receive timeout is valid only in reverse mode
IESNDTIMEOUT = 33, // Illegal message send timeout
IEUDPFILETRANSFER = 34, // Cannot transfer file using UDP
+ IESERVERAUTHUSERS = 35, // Cannot access authorized users file
+ IECNTLKA = 36, // Control connection Keepalive period should be larger than the full retry period (interval * count)
+ IEMAXSERVERTESTDURATIONEXCEEDED = 37, // Client's duration exceeds server's maximum duration
+ IEUNITVAL = 38, // Invalid unit value or suffix
/* Test errors */
IENEWTEST = 100, // Unable to create a new test (check perror)
IEINITTEST = 101, // Test initialization failed (check perror)
@@ -452,12 +495,23 @@ enum {
IEBINDDEVNOSUPPORT = 146, // `ip%%dev` is not supported as system does not support bind to device
IEHOSTDEV = 147, // host device name (ip%%) is supported (and required) only for IPv6 link-local address
IESETUSERTIMEOUT = 148, // Unable to set TCP USER_TIMEOUT (check perror)
+ IEPTHREADCREATE=150, // Unable to create thread (check perror)
+ IEPTHREADCANCEL=151, // Unable to cancel thread (check perror)
+ IEPTHREADJOIN=152, // Unable to join thread (check perror)
+ IEPTHREADATTRINIT=153, // Unable to initialize thread attribute (check perror)
+ IEPTHREADATTRDESTROY=154, // Unable to destroy thread attribute (check perror)
+ IESETCNTLKA = 155, // Unable to set socket keepalive (SO_KEEPALIVE) option
+ IESETCNTLKAKEEPIDLE = 156, // Unable to set socket keepalive TCP period (TCP_KEEPIDLE) option
+ IESETCNTLKAINTERVAL = 157, // Unable to set/get socket keepalive TCP retry interval (TCP_KEEPINTVL) option
+ IESETCNTLKACOUNT = 158, // Unable to set/get socket keepalive TCP number of retries (TCP_KEEPCNT) option
+ IEPTHREADSIGMASK=159, // Unable to initialize sub thread signal mask (check perror)
+ IESERVERTESTDURATIONEXPIRED = 160, // Server test duration expired
/* Stream errors */
IECREATESTREAM = 200, // Unable to create a new stream (check herror/perror)
IEINITSTREAM = 201, // Unable to initialize stream (check herror/perror)
IESTREAMLISTEN = 202, // Unable to start stream listener (check perror)
IESTREAMCONNECT = 203, // Unable to connect stream (check herror/perror)
- IESTREAMACCEPT = 204, // Unable to accepte stream connection (check perror)
+ IESTREAMACCEPT = 204, // Unable to accept stream connection (check perror)
IESTREAMWRITE = 205, // Unable to write to stream socket (check perror)
IESTREAMREAD = 206, // Unable to read from stream (check perror)
IESTREAMCLOSE = 207, // Stream has closed unexpectedly
diff --git a/src/iperf_auth.c b/src/iperf_auth.c
index 595f730c8..e41fe1863 100644
--- a/src/iperf_auth.c
+++ b/src/iperf_auth.c
@@ -1,5 +1,5 @@
/*
- * iperf, Copyright (c) 2014-2020, The Regents of the University of
+ * iperf, Copyright (c) 2014-2023, 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.
@@ -28,7 +28,6 @@
#include "iperf_config.h"
#include
-#include
#include
#include
/* FreeBSD needs _WITH_GETLINE to enable the getline() declaration */
@@ -46,20 +45,22 @@
#include
#include
#include
+#if OPENSSL_VERSION_MAJOR >= 3
+#include
+#include
+#endif
const char *auth_text_format = "user: %s\npwd: %s\nts: %"PRId64;
void sha256(const char *string, char outputBuffer[65])
{
unsigned char hash[SHA256_DIGEST_LENGTH];
- SHA256_CTX sha256;
- SHA256_Init(&sha256);
- SHA256_Update(&sha256, string, strlen(string));
- SHA256_Final(hash, &sha256);
+
+ SHA256((const unsigned char *) string, strlen(string), hash);
int i = 0;
for(i = 0; i < SHA256_DIGEST_LENGTH; i++)
{
- sprintf(outputBuffer + (i * 2), "%02x", hash[i]);
+ snprintf(outputBuffer + (i * 2), 3, "%02x", hash[i]);
}
outputBuffer[64] = 0;
}
@@ -73,7 +74,7 @@ int check_authentication(const char *username, const char *password, const time_
char passwordHash[65];
char salted[strlen(username) + strlen(password) + 3];
- sprintf(salted, "{%s}%s", username, password);
+ snprintf(salted, sizeof(salted), "{%s}%s", username, password);
sha256(&salted[0], passwordHash);
char *s_username, *s_password;
@@ -129,19 +130,25 @@ int Base64Encode(const unsigned char* buffer, const size_t length, char** b64tex
size_t calcDecodeLength(const char* b64input) { //Calculates the length of a decoded string
size_t len = strlen(b64input), padding = 0;
- if (b64input[len-1] == '=' && b64input[len-2] == '=') //last two chars are =
+ if (len >= 2 && b64input[len-1] == '=' && b64input[len-2] == '=') //last two chars are =
padding = 2;
- else if (b64input[len-1] == '=') //last char is =
+ else if (len >= 1 && b64input[len-1] == '=') //last char is =
padding = 1;
- return (len*3)/4 - padding;
+ size_t decoded_len = (len*3)/4;
+ if (padding > decoded_len) {
+ return 0;
+ }
+ return decoded_len - padding;
}
int Base64Decode(const char* b64message, unsigned char** buffer, size_t* length) { //Decodes a base64 encoded string
BIO *bio, *b64;
- int decodeLen = calcDecodeLength(b64message);
+ size_t decodeLen = calcDecodeLength(b64message);
*buffer = (unsigned char*)malloc(decodeLen + 1);
+ if (!*buffer)
+ return -1;
(*buffer)[decodeLen] = '\0';
bio = BIO_new_mem_buf(b64message, -1);
@@ -150,7 +157,6 @@ int Base64Decode(const char* b64message, unsigned char** buffer, size_t* length)
BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL); //Do not use newlines to flush buffer
*length = BIO_read(bio, *buffer, strlen(b64message));
- assert(*length == decodeLen); //length should equal decodeLen, else something went horribly wrong
BIO_free_all(bio);
return (0); //success
@@ -228,61 +234,140 @@ int test_load_private_key_from_file(const char *file){
return 0;
}
-int encrypt_rsa_message(const char *plaintext, EVP_PKEY *public_key, unsigned char **encryptedtext) {
+int encrypt_rsa_message(const char *plaintext, EVP_PKEY *public_key, unsigned char **encryptedtext, int use_pkcs1_padding) {
+#if OPENSSL_VERSION_MAJOR >= 3
+ EVP_PKEY_CTX *ctx;
+#else
RSA *rsa = NULL;
- unsigned char *rsa_buffer = NULL, pad = RSA_PKCS1_PADDING;
- int keysize, encryptedtext_len, rsa_buffer_len;
-
+#endif
+ unsigned char *rsa_buffer = NULL;
+ size_t encryptedtext_len = 0, plaintext_len = 0;
+ int rsa_buffer_len, output_buffer_len;
+
+#if OPENSSL_VERSION_MAJOR >= 3
+ int rc;
+ ctx = EVP_PKEY_CTX_new_from_pkey(NULL, public_key, "");
+ /* See evp_pkey_rsa(7) and provider-keymgmt(7) */
+ rc = EVP_PKEY_get_int_param(public_key, OSSL_PKEY_PARAM_MAX_SIZE, &output_buffer_len);
+ if (!rc) {
+ goto errreturn;
+ }
+#else
rsa = EVP_PKEY_get1_RSA(public_key);
- keysize = RSA_size(rsa);
-
- rsa_buffer = OPENSSL_malloc(keysize * 2);
- *encryptedtext = (unsigned char*)OPENSSL_malloc(keysize);
+ output_buffer_len = RSA_size(rsa);
+#endif
+ plaintext_len = strlen(plaintext);
+ if (plaintext_len > output_buffer_len) {
+ fprintf(stderr, "Plaintext of size %zd truncated to %d; data is lost.\n", plaintext_len, output_buffer_len);
+ }
+ rsa_buffer = OPENSSL_malloc(output_buffer_len);
+ *encryptedtext = (unsigned char*)OPENSSL_malloc(output_buffer_len);
+ encryptedtext_len = output_buffer_len;
- BIO *bioBuff = BIO_new_mem_buf((void*)plaintext, (int)strlen(plaintext));
- rsa_buffer_len = BIO_read(bioBuff, rsa_buffer, keysize * 2);
- encryptedtext_len = RSA_public_encrypt(rsa_buffer_len, rsa_buffer, *encryptedtext, rsa, pad);
+ BIO *bioBuff = BIO_new_mem_buf((void*)plaintext, (int)plaintext_len);
+ rsa_buffer_len = BIO_read(bioBuff, rsa_buffer, plaintext_len);
+ int padding = RSA_PKCS1_OAEP_PADDING;
+ if (use_pkcs1_padding){
+ padding = RSA_PKCS1_PADDING;
+ }
+#if OPENSSL_VERSION_MAJOR >= 3
+ EVP_PKEY_encrypt_init(ctx);
+ EVP_PKEY_CTX_set_rsa_padding(ctx, padding);
+
+ EVP_PKEY_encrypt(ctx, *encryptedtext, &encryptedtext_len, rsa_buffer, rsa_buffer_len);
+ EVP_PKEY_CTX_free(ctx);
+#else
+ int encrypt_ret = RSA_public_encrypt(rsa_buffer_len, rsa_buffer, *encryptedtext, rsa, padding);
+ encryptedtext_len = encrypt_ret < 0 ? 0 : (size_t) encrypt_ret;
RSA_free(rsa);
+#endif
+
OPENSSL_free(rsa_buffer);
BIO_free(bioBuff);
- if (encryptedtext_len < 0) {
- /* We probably shouldn't be printing stuff like this */
- fprintf(stderr, "%s\n", ERR_error_string(ERR_get_error(), NULL));
+ if (encryptedtext_len <= 0) {
+ goto errreturn;
}
return encryptedtext_len;
+
+ errreturn:
+ fprintf(stderr, "%s\n", ERR_error_string(ERR_get_error(), NULL));
+ return 0;
}
-int decrypt_rsa_message(const unsigned char *encryptedtext, const int encryptedtext_len, EVP_PKEY *private_key, unsigned char **plaintext) {
+int decrypt_rsa_message(const unsigned char *encryptedtext, const int encryptedtext_len, EVP_PKEY *private_key, unsigned char **plaintext, int use_pkcs1_padding) {
+#if OPENSSL_VERSION_MAJOR >= 3
+ EVP_PKEY_CTX *ctx;
+#else
RSA *rsa = NULL;
- unsigned char *rsa_buffer = NULL, pad = RSA_PKCS1_PADDING;
- int plaintext_len, rsa_buffer_len, keysize;
-
+#endif
+ unsigned char *rsa_buffer = NULL;
+ size_t plaintext_len = 0;
+ int rsa_buffer_len, output_buffer_len;
+
+#if OPENSSL_VERSION_MAJOR >= 3
+ int rc;
+ ctx = EVP_PKEY_CTX_new_from_pkey(NULL, private_key, "");
+ /* See evp_pkey_rsa(7) and provider-keymgmt(7) */
+ rc = EVP_PKEY_get_int_param(private_key, OSSL_PKEY_PARAM_MAX_SIZE, &output_buffer_len);
+ if (!rc) {
+ goto errreturn;
+ }
+#else
rsa = EVP_PKEY_get1_RSA(private_key);
-
- keysize = RSA_size(rsa);
- rsa_buffer = OPENSSL_malloc(keysize * 2);
- *plaintext = (unsigned char*)OPENSSL_malloc(keysize);
+ output_buffer_len = RSA_size(rsa);
+#endif
+ if (encryptedtext_len > output_buffer_len) {
+ fprintf(stderr, "Encrypted text of size %d truncated to %d; likely invalid input.\n", encryptedtext_len, output_buffer_len);
+ }
+ rsa_buffer = OPENSSL_malloc(output_buffer_len);
+ // Note: +1 for NULL
+ *plaintext = (unsigned char*)OPENSSL_malloc(output_buffer_len + 1);
BIO *bioBuff = BIO_new_mem_buf((void*)encryptedtext, encryptedtext_len);
- rsa_buffer_len = BIO_read(bioBuff, rsa_buffer, keysize * 2);
- plaintext_len = RSA_private_decrypt(rsa_buffer_len, rsa_buffer, *plaintext, rsa, pad);
+ rsa_buffer_len = BIO_read(bioBuff, rsa_buffer, encryptedtext_len);
+ int padding = RSA_PKCS1_OAEP_PADDING;
+ if (use_pkcs1_padding){
+ padding = RSA_PKCS1_PADDING;
+ }
+#if OPENSSL_VERSION_MAJOR >= 3
+ int ret = 0;
+ plaintext_len = output_buffer_len;
+ EVP_PKEY_decrypt_init(ctx);
+
+ ret = EVP_PKEY_CTX_set_rsa_padding(ctx, padding);
+ if (ret < 0){
+ goto errreturn;
+ }
+ ret = EVP_PKEY_decrypt(ctx, *plaintext, &plaintext_len, rsa_buffer, rsa_buffer_len);
+ EVP_PKEY_CTX_free(ctx);
+#else
+ int decrypt_ret = RSA_private_decrypt(rsa_buffer_len, rsa_buffer, *plaintext, rsa, padding);
+ plaintext_len = decrypt_ret < 0 ? 0 : (size_t) decrypt_ret;
RSA_free(rsa);
+#endif
+
OPENSSL_free(rsa_buffer);
BIO_free(bioBuff);
- if (plaintext_len < 0) {
- /* We probably shouldn't be printing stuff like this */
- fprintf(stderr, "%s\n", ERR_error_string(ERR_get_error(), NULL));
+ /* Treat a decryption error as an empty string. */
+ if (plaintext_len <= 0) {
+ plaintext_len = 0;
}
return plaintext_len;
+
+#if OPENSSL_VERSION_MAJOR >= 3
+ errreturn:
+ fprintf(stderr, "%s\n", ERR_error_string(ERR_get_error(), NULL));
+ return 0;
+#endif
}
-int encode_auth_setting(const char *username, const char *password, EVP_PKEY *public_key, char **authtoken){
+int encode_auth_setting(const char *username, const char *password, EVP_PKEY *public_key, char **authtoken, int use_pkcs1_padding){
time_t t = time(NULL);
time_t utc_seconds = mktime(localtime(&t));
@@ -293,13 +378,13 @@ int encode_auth_setting(const char *username, const char *password, EVP_PKEY *pu
const int text_len = strlen(auth_text_format) + strlen(username) + strlen(password) + 32;
char *text = (char *) calloc(text_len, sizeof(char));
if (text == NULL) {
- return -1;
+ return -1;
}
snprintf(text, text_len, auth_text_format, username, password, (int64_t)utc_seconds);
unsigned char *encrypted = NULL;
int encrypted_len;
- encrypted_len = encrypt_rsa_message(text, public_key, &encrypted);
+ encrypted_len = encrypt_rsa_message(text, public_key, &encrypted, use_pkcs1_padding);
free(text);
if (encrypted_len < 0) {
return -1;
@@ -310,37 +395,41 @@ int encode_auth_setting(const char *username, const char *password, EVP_PKEY *pu
return (0); //success
}
-int decode_auth_setting(int enable_debug, const 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, int use_pkcs1_padding){
unsigned char *encrypted_b64 = NULL;
size_t encrypted_len_b64;
- int64_t utc_seconds;
+ int64_t utc_seconds =0;
Base64Decode(authtoken, &encrypted_b64, &encrypted_len_b64);
unsigned char *plaintext = NULL;
int plaintext_len;
- plaintext_len = decrypt_rsa_message(encrypted_b64, encrypted_len_b64, private_key, &plaintext);
+ plaintext_len = decrypt_rsa_message(encrypted_b64, encrypted_len_b64, private_key, &plaintext, use_pkcs1_padding);
free(encrypted_b64);
- if (plaintext_len < 0) {
+ if (plaintext_len <= 0) {
return -1;
}
+
plaintext[plaintext_len] = '\0';
char *s_username, *s_password;
s_username = (char *) calloc(plaintext_len, sizeof(char));
if (s_username == NULL) {
- return -1;
+ OPENSSL_free(plaintext);
+ return -1;
}
s_password = (char *) calloc(plaintext_len, sizeof(char));
if (s_password == NULL) {
- free(s_username);
- return -1;
+ OPENSSL_free(plaintext);
+ free(s_username);
+ return -1;
}
int rc = sscanf((char *) plaintext, auth_text_format, s_username, s_password, &utc_seconds);
if (rc != 3) {
- free(s_password);
- free(s_username);
- return -1;
+ OPENSSL_free(plaintext);
+ free(s_password);
+ free(s_username);
+ return -1;
}
if (enable_debug) {
diff --git a/src/iperf_auth.h b/src/iperf_auth.h
index ffadbf3e5..eedd45abd 100644
--- a/src/iperf_auth.h
+++ b/src/iperf_auth.h
@@ -35,7 +35,7 @@ EVP_PKEY *load_pubkey_from_file(const char *file);
EVP_PKEY *load_pubkey_from_base64(const char *buffer);
EVP_PKEY *load_privkey_from_file(const char *file);
EVP_PKEY *load_privkey_from_base64(const char *buffer);
-int encode_auth_setting(const char *username, const char *password, EVP_PKEY *public_key, char **authtoken);
-int decode_auth_setting(int enable_debug, const char *authtoken, EVP_PKEY *private_key, char **username, char **password, time_t *ts);
+int encode_auth_setting(const char *username, const char *password, EVP_PKEY *public_key, char **authtoken, int use_pkcs1_padding);
+int decode_auth_setting(int enable_debug, const char *authtoken, EVP_PKEY *private_key, char **username, char **password, time_t *ts, int use_pkcs1_padding);
int check_authentication(const char *username, const char *password, const time_t ts, const char *filename, int skew_threshold);
ssize_t iperf_getpass (char **lineptr, size_t *n, FILE *stream);
diff --git a/src/iperf_client_api.c b/src/iperf_client_api.c
index 583306880..23ca67f61 100644
--- a/src/iperf_client_api.c
+++ b/src/iperf_client_api.c
@@ -1,5 +1,5 @@
/*
- * iperf, Copyright (c) 2014-2022, The Regents of the University of
+ * iperf, Copyright (c) 2014-2023, 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.
@@ -36,6 +36,7 @@
#include
#include
#include
+#include
#include "iperf.h"
#include "iperf_api.h"
@@ -51,6 +52,50 @@
#endif /* TCP_CA_NAME_MAX */
#endif /* HAVE_TCP_CONGESTION */
+void *
+iperf_client_worker_run(void *s) {
+ struct iperf_stream *sp = (struct iperf_stream *) s;
+ struct iperf_test *test = sp->test;
+
+ /* Blocking signal to make sure that signal will be handled by main thread */
+ sigset_t set;
+ sigemptyset(&set);
+#ifdef SIGTERM
+ sigaddset(&set, SIGTERM);
+#endif
+#ifdef SIGHUP
+ sigaddset(&set, SIGHUP);
+#endif
+#ifdef SIGINT
+ sigaddset(&set, SIGINT);
+#endif
+ if (pthread_sigmask(SIG_BLOCK, &set, NULL) != 0) {
+ i_errno = IEPTHREADSIGMASK;
+ goto cleanup_and_fail;
+ }
+
+ /* Allow this thread to be cancelled even if it's in a syscall */
+ pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
+ pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
+
+ while (! (test->done) && ! (sp->done)) {
+ if (sp->sender) {
+ if (iperf_send_mt(sp) < 0) {
+ goto cleanup_and_fail;
+ }
+ }
+ else {
+ if (iperf_recv_mt(sp) < 0) {
+ goto cleanup_and_fail;
+ }
+ }
+ }
+ return NULL;
+
+ cleanup_and_fail:
+ return NULL;
+}
+
int
iperf_create_streams(struct iperf_test *test, int sender)
{
@@ -103,6 +148,11 @@ iperf_create_streams(struct iperf_test *test, int sender)
i_errno = IESETCONGESTION;
return -1;
}
+ if (test->congestion_used) {
+ if (test->debug)
+ printf("Overriding existing congestion algorithm: %s\n", test->congestion_used);
+ free(test->congestion_used);
+ }
// Set actual used congestion alg, or set to unknown if could not get it
if (rc < 0)
test->congestion_used = strdup("unknown");
@@ -115,12 +165,6 @@ iperf_create_streams(struct iperf_test *test, int sender)
}
#endif /* HAVE_TCP_CONGESTION */
- if (sender)
- FD_SET(s, &test->write_set);
- else
- FD_SET(s, &test->read_set);
- if (s > test->max_fd) test->max_fd = s;
-
sp = iperf_new_stream(test, s, sender);
if (!sp)
return -1;
@@ -267,6 +311,11 @@ iperf_handle_message_client(struct iperf_test *test)
i_errno = IEINITTEST;
return -1;
}
+
+ if (test->debug_level >= DEBUG_LEVEL_INFO) {
+ iperf_printf(test, "Reading new State from the Server - current state is %d-%s\n", test->state, state_to_text(test->state));
+ }
+
/*!!! Why is this read() and not Nread()? */
if ((rval = read(test->ctrl_sck, (char*) &test->state, sizeof(signed char))) <= 0) {
if (rval == 0) {
@@ -278,6 +327,10 @@ iperf_handle_message_client(struct iperf_test *test)
}
}
+ if (test->debug_level >= DEBUG_LEVEL_INFO) {
+ iperf_printf(test, "State change: client received and changed State to %d-%s\n", test->state, state_to_text(test->state));
+ }
+
switch (test->state) {
case PARAM_EXCHANGE:
if (iperf_exchange_parameters(test) < 0)
@@ -347,6 +400,11 @@ iperf_handle_message_client(struct iperf_test *test)
return -1;
}
errno = ntohl(err);
+ if (errno > 0) {
+ iperf_err(test, "SERVER ERROR - %s, errno: %s", iperf_strerror(i_errno), strerror(errno));
+ } else {
+ iperf_err(test, "SERVER ERROR - %s", iperf_strerror(i_errno));
+ }
return -1;
default:
i_errno = IEMESSAGE;
@@ -362,7 +420,7 @@ iperf_handle_message_client(struct iperf_test *test)
int
iperf_connect(struct iperf_test *test)
{
- int opt;
+ int opt, n;
socklen_t len;
if (NULL == test)
@@ -391,11 +449,17 @@ iperf_connect(struct iperf_test *test)
return -1;
}
+#if defined (HAVE_TCP_KEEPALIVE)
+ // Set Control Connection TCP Keepalive (especially useful for long UDP test sessions)
+ if (iperf_set_control_keepalive(test) < 0)
+ return -1;
+#endif //HAVE_TCP_KEEPALIVE
+
#if defined(HAVE_TCP_USER_TIMEOUT)
if ((opt = test->settings->snd_timeout)) {
if (setsockopt(test->ctrl_sck, IPPROTO_TCP, TCP_USER_TIMEOUT, &opt, sizeof(opt)) < 0) {
- i_errno = IESETUSERTIMEOUT;
- return -1;
+ i_errno = IESETUSERTIMEOUT;
+ return -1;
}
}
#endif /* HAVE_TCP_USER_TIMEOUT */
@@ -457,6 +521,21 @@ iperf_connect(struct iperf_test *test)
printf("Setting UDP block size to %d\n", test->settings->blksize);
}
}
+ /* Initialize GSO parameters when --gsro is used */
+ if (test->settings->gso) {
+ test->settings->gso_dg_size = test->settings->blksize;
+ /* use the multiple of datagram size for the best efficiency. */
+ if (test->settings->gso_dg_size > 0) {
+ n = test->settings->gso_bf_size / test->settings->gso_dg_size;
+ if (n > GSO_MAX_DG_IN_BF) {
+ n = GSO_MAX_DG_IN_BF;
+ }
+ test->settings->gso_bf_size = n * test->settings->gso_dg_size;
+ } else {
+ /* If gso_dg_size is 0 (unlimited bandwidth), use default UDP datagram size */
+ test->settings->gso_dg_size = DEFAULT_UDP_BLKSIZE;
+ }
+ }
/*
* Regardless of whether explicitly or implicitly set, if the
@@ -500,8 +579,10 @@ iperf_client_end(struct iperf_test *test)
}
/* Close control socket */
- if (test->ctrl_sck >= 0)
- close(test->ctrl_sck);
+ if (test->ctrl_sck >= 0) {
+ // Make sure all control messages are received by the server before the socket is closed
+ iperf_sync_close_socket(test->ctrl_sck);
+ }
return 0;
}
@@ -519,9 +600,11 @@ iperf_run_client(struct iperf_test * test)
struct iperf_time last_receive_time;
struct iperf_time diff_time;
struct timeval used_timeout;
+ iperf_size_t last_receive_blocks;
int64_t t_usecs;
int64_t timeout_us;
int64_t rcv_timeout_us;
+ int i_errno_save;
if (NULL == test)
{
@@ -562,6 +645,9 @@ iperf_run_client(struct iperf_test * test)
else
rcv_timeout_us = 0;
+ iperf_time_now(&last_receive_time); // Initialize last time something was received
+ last_receive_blocks = 0;
+
startup = 1;
while (test->state != IPERF_DONE) {
memcpy(&read_set, &test->read_set, sizeof(fd_set));
@@ -577,6 +663,10 @@ iperf_run_client(struct iperf_test * test)
used_timeout.tv_usec = timeout->tv_usec;
timeout_us = (timeout->tv_sec * SEC_TO_US) + timeout->tv_usec;
}
+ /* Cap the maximum select timeout at 1 second */
+ if (timeout_us > SEC_TO_US) {
+ timeout_us = SEC_TO_US;
+ }
if (timeout_us < 0 || timeout_us > rcv_timeout_us) {
used_timeout.tv_sec = test->settings->rcv_timeout.secs;
used_timeout.tv_usec = test->settings->rcv_timeout.usecs;
@@ -584,28 +674,49 @@ iperf_run_client(struct iperf_test * test)
timeout = &used_timeout;
}
+#if (defined(__vxworks)) || (defined(__VXWORKS__))
+ if (timeout != NULL && timeout->tv_sec == 0 && timeout->tv_usec == 0) {
+ taskDelay (1);
+ }
+
+ result = select(test->max_fd + 1,
+ &read_set,
+ (test->state == TEST_RUNNING && !test->reverse) ? &write_set : NULL,
+ NULL,
+ timeout);
+#else
result = select(test->max_fd + 1, &read_set, &write_set, NULL, timeout);
+#endif // __vxworks or __VXWORKS__
if (result < 0 && errno != EINTR) {
i_errno = IESELECT;
goto cleanup_and_fail;
} else if (result == 0 && test->state == TEST_RUNNING && rcv_timeout_us > 0) {
- // If nothing was received in non-reverse running state then probably something got stack -
- // either client, server or network, and test should be terminated.
+ /*
+ * If nothing was received in non-reverse running state
+ * then probably something got stuck - either client,
+ * server or network, and test should be terminated./
+ */
iperf_time_now(&now);
if (iperf_time_diff(&now, &last_receive_time, &diff_time) == 0) {
t_usecs = iperf_time_in_usecs(&diff_time);
if (t_usecs > rcv_timeout_us) {
- i_errno = IENOMSG;
- goto cleanup_and_fail;
+ /* Idle timeout if no new blocks received */
+ if (test->blocks_received == last_receive_blocks) {
+ i_errno = IENOMSG;
+ goto cleanup_and_fail;
+ }
}
}
}
+ /* See if the test is making progress */
+ if (test->blocks_received > last_receive_blocks) {
+ last_receive_blocks = test->blocks_received;
+ last_receive_time = now;
+ }
+
if (result > 0) {
- if (rcv_timeout_us > 0) {
- iperf_time_now(&last_receive_time);
- }
if (FD_ISSET(test->ctrl_sck, &read_set)) {
if (iperf_handle_message_client(test) < 0) {
goto cleanup_and_fail;
@@ -620,31 +731,32 @@ iperf_run_client(struct iperf_test * test)
if (startup) {
startup = 0;
- // Set non-blocking for non-UDP tests
- if (test->protocol->id != Pudp) {
- SLIST_FOREACH(sp, &test->streams, streams) {
- setnonblocking(sp->socket, 1);
- }
- }
- }
-
-
- if (test->mode == BIDIRECTIONAL)
- {
- if (iperf_send(test, &write_set) < 0)
- goto cleanup_and_fail;
- if (iperf_recv(test, &read_set) < 0)
- goto cleanup_and_fail;
- } else if (test->mode == SENDER) {
- // Regular mode. Client sends.
- if (iperf_send(test, &write_set) < 0)
+ /* Create and spin up threads */
+ pthread_attr_t attr;
+ if (pthread_attr_init(&attr) != 0) {
+ i_errno = IEPTHREADATTRINIT;
goto cleanup_and_fail;
- } else {
- // Reverse mode. Client receives.
- if (iperf_recv(test, &read_set) < 0)
+ }
+
+ SLIST_FOREACH(sp, &test->streams, streams) {
+ if (pthread_create(&(sp->thr), &attr, &iperf_client_worker_run, sp) != 0) {
+ i_errno = IEPTHREADCREATE;
+ goto cleanup_and_fail;
+ }
+ sp->thread_created = 1;
+ if (test->debug_level >= DEBUG_LEVEL_INFO) {
+ iperf_printf(test, "Thread FD %d created\n", sp->socket);
+ }
+ }
+ if (test->debug_level >= DEBUG_LEVEL_INFO) {
+ iperf_printf(test, "All threads created\n");
+ }
+ if (pthread_attr_destroy(&attr) != 0) {
+ i_errno = IEPTHREADATTRDESTROY;
goto cleanup_and_fail;
- }
+ }
+ }
/* Run the timers. */
iperf_time_now(&now);
@@ -665,12 +777,50 @@ iperf_run_client(struct iperf_test * test)
(test->settings->blocks != 0 && (test->blocks_sent >= test->settings->blocks ||
test->blocks_received >= test->settings->blocks)))) {
- // Unset non-blocking for non-UDP tests
- if (test->protocol->id != Pudp) {
- SLIST_FOREACH(sp, &test->streams, streams) {
- setnonblocking(sp->socket, 0);
- }
- }
+ /*
+ * Cancel the periodic timers. As the timers are not re-set in this stage their expiration time
+ * is in the past, so without the cancellation the select() timeout is set to 0,
+ * and it enters a loop while waiting the exchanged results, etc.
+ */
+ if (test->stats_timer != NULL) {
+ tmr_cancel(test->stats_timer);
+ test->stats_timer = NULL;
+ }
+ if (test->reporter_timer != NULL) {
+ tmr_cancel(test->reporter_timer);
+ test->reporter_timer = NULL;
+ }
+
+ /* Cancel outstanding sender threads */
+ SLIST_FOREACH(sp, &test->streams, streams) {
+ if (sp->sender) {
+ int rc;
+ sp->done = 1;
+ if (sp->thread_created == 1) {
+ rc = pthread_cancel(sp->thr);
+ if (rc != 0 && rc != ESRCH) {
+ i_errno = IEPTHREADCANCEL;
+ errno = rc;
+ iperf_err(test, "sender cancel in pthread_cancel - %s", iperf_strerror(i_errno));
+ goto cleanup_and_fail;
+ }
+ rc = pthread_join(sp->thr, NULL);
+ if (rc != 0 && rc != ESRCH) {
+ i_errno = IEPTHREADJOIN;
+ errno = rc;
+ iperf_err(test, "sender cancel in pthread_join - %s", iperf_strerror(i_errno));
+ goto cleanup_and_fail;
+ }
+ if (test->debug_level >= DEBUG_LEVEL_INFO) {
+ iperf_printf(test, "Thread FD %d stopped\n", sp->socket);
+ }
+ sp->thread_created = 0;
+ }
+ }
+ }
+ if (test->debug_level >= DEBUG_LEVEL_INFO) {
+ iperf_printf(test, "Sender threads stopped\n");
+ }
/* Yes, done! Send TEST_END. */
test->done = 1;
@@ -680,15 +830,37 @@ iperf_run_client(struct iperf_test * test)
goto cleanup_and_fail;
}
}
- // If we're in reverse mode, continue draining the data
- // connection(s) even if test is over. This prevents a
- // deadlock where the server side fills up its pipe(s)
- // and gets blocked, so it can't receive state changes
- // from the client side.
- else if (test->mode == RECEIVER && test->state == TEST_END) {
- if (iperf_recv(test, &read_set) < 0)
- goto cleanup_and_fail;
- }
+ }
+
+ /* Cancel outstanding receiver threads */
+ SLIST_FOREACH(sp, &test->streams, streams) {
+ if (!sp->sender) {
+ int rc;
+ sp->done = 1;
+ if (sp->thread_created == 1) {
+ rc = pthread_cancel(sp->thr);
+ if (rc != 0 && rc != ESRCH) {
+ i_errno = IEPTHREADCANCEL;
+ errno = rc;
+ iperf_err(test, "receiver cancel in pthread_cancel - %s", iperf_strerror(i_errno));
+ goto cleanup_and_fail;
+ }
+ rc = pthread_join(sp->thr, NULL);
+ if (rc != 0 && rc != ESRCH) {
+ i_errno = IEPTHREADJOIN;
+ errno = rc;
+ iperf_err(test, "receiver cancel in pthread_join - %s", iperf_strerror(i_errno));
+ goto cleanup_and_fail;
+ }
+ if (test->debug_level >= DEBUG_LEVEL_INFO) {
+ iperf_printf(test, "Thread FD %d stopped\n", sp->socket);
+ }
+ sp->thread_created = 0;
+ }
+ }
+ }
+ if (test->debug_level >= DEBUG_LEVEL_INFO) {
+ iperf_printf(test, "Receiver threads stopped\n");
}
if (test->json_output) {
@@ -704,14 +876,42 @@ iperf_run_client(struct iperf_test * test)
return 0;
cleanup_and_fail:
+ /* Cancel all outstanding threads */
+ i_errno_save = i_errno;
+ SLIST_FOREACH(sp, &test->streams, streams) {
+ if (sp->done) {
+ continue;
+ }
+ sp->done = 1;
+ int rc;
+ if (sp->thread_created == 1) {
+ rc = pthread_cancel(sp->thr);
+ if (rc != 0 && rc != ESRCH) {
+ i_errno = IEPTHREADCANCEL;
+ errno = rc;
+ iperf_err(test, "cleanup_and_fail in pthread_cancel - %s", iperf_strerror(i_errno));
+ }
+ rc = pthread_join(sp->thr, NULL);
+ if (rc != 0 && rc != ESRCH) {
+ i_errno = IEPTHREADJOIN;
+ errno = rc;
+ iperf_err(test, "cleanup_and_fail in pthread_join - %s", iperf_strerror(i_errno));
+ }
+ if (test->debug_level >= DEBUG_LEVEL_INFO) {
+ iperf_printf(test, "Thread FD %d stopped\n", sp->socket);
+ }
+ sp->thread_created = 0;
+ }
+ }
+ if (test->debug_level >= DEBUG_LEVEL_INFO) {
+ iperf_printf(test, "All threads stopped\n");
+ }
+ i_errno = i_errno_save;
+
iperf_client_end(test);
if (test->json_output) {
cJSON_AddStringToObject(test->json_top, "error", iperf_strerror(i_errno));
iperf_json_finish(test);
- iflush(test);
- // Return 0 and not -1 since all terminating function were done here.
- // Also prevents error message logging outside the already closed JSON output.
- return 0;
}
iflush(test);
return -1;
diff --git a/src/iperf_config.h.in b/src/iperf_config.h.in
index 883107901..28a6c4e82 100644
--- a/src/iperf_config.h.in
+++ b/src/iperf_config.h.in
@@ -1,15 +1,21 @@
/* src/iperf_config.h.in. Generated from configure.ac by autoheader. */
-/* Define to 1 if you have the `clock_gettime' function. */
+/* Can bind to device via SO_BINDTODEVICE or IP_BOUND_IF. */
+#undef CAN_BIND_TO_DEVICE
+
+/* Define to 1 if you have the 'clock_gettime' function. */
#undef HAVE_CLOCK_GETTIME
-/* Define to 1 if you have the `cpuset_setaffinity' function. */
+/* Define to 1 if you have the 'clock_nanosleep' function. */
+#undef HAVE_CLOCK_NANOSLEEP
+
+/* Define to 1 if you have the 'cpuset_setaffinity' function. */
#undef HAVE_CPUSET_SETAFFINITY
/* Have CPU affinity support. */
#undef HAVE_CPU_AFFINITY
-/* Define to 1 if you have the `daemon' function. */
+/* Define to 1 if you have the 'daemon' function. */
#undef HAVE_DAEMON
/* Define to 1 if you have the header file. */
@@ -24,12 +30,18 @@
/* Have IPv6 flowlabel support. */
#undef HAVE_FLOWLABEL
-/* Define to 1 if you have the `getline' function. */
+/* Define to 1 if you have the 'getline' function. */
#undef HAVE_GETLINE
/* Define to 1 if you have the header file. */
#undef HAVE_INTTYPES_H
+/* Have MPTCP protocol. */
+#undef HAVE_IPPROTO_MPTCP
+
+/* Have IP_BOUND_IF sockopt. */
+#undef HAVE_IP_BOUND_IF
+
/* Have IP_DONTFRAG sockopt. */
#undef HAVE_IP_DONTFRAG
@@ -42,24 +54,39 @@
/* Define to 1 if you have the header file. */
#undef HAVE_LINUX_TCP_H
+/* Have MSG_TRUNC recv option. */
+#undef HAVE_MSG_TRUNC
+
+/* Define to 1 if you have the 'nanosleep' function. */
+#undef HAVE_NANOSLEEP
+
/* Define to 1 if you have the header file. */
#undef HAVE_NETINET_SCTP_H
/* Define to 1 if you have the header file. */
#undef HAVE_POLL_H
-/* Define to 1 if you have the `sched_setaffinity' function. */
+/* Define if you have POSIX threads libraries and header files. */
+#undef HAVE_PTHREAD
+
+/* Have PTHREAD_PRIO_INHERIT. */
+#undef HAVE_PTHREAD_PRIO_INHERIT
+
+/* Define to 1 if you have the 'sched_setaffinity' function. */
#undef HAVE_SCHED_SETAFFINITY
/* Have SCTP support. */
#undef HAVE_SCTP_H
-/* Define to 1 if you have the `sendfile' function. */
+/* Define to 1 if you have the 'sendfile' function. */
#undef HAVE_SENDFILE
-/* Define to 1 if you have the `SetProcessAffinityMask' function. */
+/* Define to 1 if you have the 'SetProcessAffinityMask' function. */
#undef HAVE_SETPROCESSAFFINITYMASK
+/* Have SHUT_WR socket shutdown option. */
+#undef HAVE_SOCKET_SHUTDOWN_SHUT_WR
+
/* Have SO_BINDTODEVICE sockopt. */
#undef HAVE_SO_BINDTODEVICE
@@ -69,6 +96,9 @@
/* OpenSSL Is Available */
#undef HAVE_SSL
+/* Define to 1 if you have the header file. */
+#undef HAVE_STDATOMIC_H
+
/* Define to 1 if you have the header file. */
#undef HAVE_STDINT_H
@@ -84,7 +114,7 @@
/* Define to 1 if you have the header file. */
#undef HAVE_STRING_H
-/* Define to 1 if the system has the type `struct sctp_assoc_value'. */
+/* Define to 1 if the system has the type 'struct sctp_assoc_value'. */
#undef HAVE_STRUCT_SCTP_ASSOC_VALUE
/* Define to 1 if you have the header file. */
@@ -105,9 +135,18 @@
/* Have tcpi_snd_wnd field in tcp_info. */
#undef HAVE_TCP_INFO_SND_WND
+/* Have TCP_KEEPIDLE sockopt. */
+#undef HAVE_TCP_KEEPALIVE
+
/* Have TCP_USER_TIMEOUT sockopt. */
#undef HAVE_TCP_USER_TIMEOUT
+/* Have UDP_GRO sockopt. */
+#undef HAVE_UDP_GRO
+
+/* Have UDP_SEGMENT sockopt. */
+#undef HAVE_UDP_SEGMENT
+
/* Define to 1 if you have the header file. */
#undef HAVE_UNISTD_H
@@ -135,7 +174,11 @@
/* Define to the version of this package. */
#undef PACKAGE_VERSION
-/* Define to 1 if all of the C90 standard headers exist (not just the ones
+/* Define to necessary symbol if this constant uses a non-standard name on
+ your system. */
+#undef PTHREAD_CREATE_JOINABLE
+
+/* Define to 1 if all of the C89 standard headers exist (not just the ones
required in a freestanding environment). This macro is provided for
backward compatibility; new code need not use it. */
#undef STDC_HEADERS
@@ -143,5 +186,5 @@
/* Version number of package */
#undef VERSION
-/* Define to empty if `const' does not conform to ANSI C. */
+/* Define to empty if 'const' does not conform to ANSI C. */
#undef const
diff --git a/src/iperf_error.c b/src/iperf_error.c
index 13e9c1511..40ca492ea 100644
--- a/src/iperf_error.c
+++ b/src/iperf_error.c
@@ -60,7 +60,11 @@ iperf_err(struct iperf_test *test, const char *format, ...)
if (test != NULL && test->json_output && test->json_top != NULL)
cJSON_AddStringToObject(test->json_top, "error", str);
else {
- if (test && test->outfile && test->outfile != stdout) {
+ if (test != NULL && pthread_mutex_lock(&(test->print_mutex)) != 0) {
+ perror("iperf_err: pthread_mutex_lock");
+ }
+
+ if (test != NULL && test->outfile != NULL && test->outfile != stdout) {
if (ct) {
fprintf(test->outfile, "%s", ct);
}
@@ -72,15 +76,39 @@ iperf_err(struct iperf_test *test, const char *format, ...)
}
fprintf(stderr, "iperf3: %s\n", str);
}
+
+ if (test != NULL && pthread_mutex_unlock(&(test->print_mutex)) != 0) {
+ perror("iperf_err: pthread_mutex_unlock");
+ }
+
}
va_end(argp);
}
-/* Do a printf to stderr or log file as appropriate, then exit. */
+/* Do a printf to stderr or log file as appropriate, then exit(0). */
+void
+iperf_signormalexit(struct iperf_test *test, const char *format, ...)
+{
+ va_list argp;
+
+ va_start(argp, format);
+ iperf_exit(test, 0, format, argp);
+}
+
+/* Do a printf to stderr or log file as appropriate, then exit(1). */
void
iperf_errexit(struct iperf_test *test, const char *format, ...)
{
va_list argp;
+
+ va_start(argp, format);
+ iperf_exit(test, 1, format, argp);
+}
+
+/* Do a printf to stderr or log file as appropriate, then exit. */
+void
+iperf_exit(struct iperf_test *test, int exit_code, const char *format, va_list argp)
+{
char str[1000];
time_t now;
struct tm *ltm = NULL;
@@ -90,16 +118,21 @@ iperf_errexit(struct iperf_test *test, const char *format, ...)
if (test != NULL && test->timestamps) {
time(&now);
ltm = localtime(&now);
- strftime(iperf_timestrerr, sizeof(iperf_timestrerr), "%c ", ltm);
+ strftime(iperf_timestrerr, sizeof(iperf_timestrerr), iperf_get_test_timestamp_format(test), ltm);
ct = iperf_timestrerr;
}
- va_start(argp, format);
vsnprintf(str, sizeof(str), format, argp);
- if (test != NULL && test->json_output && test->json_top != NULL) {
- cJSON_AddStringToObject(test->json_top, "error", str);
+ if (test != NULL && test->json_output) {
+ if (test->json_top != NULL) {
+ cJSON_AddStringToObject(test->json_top, "error", str);
+ }
iperf_json_finish(test);
- } else
+ } else {
+ if (test != NULL && pthread_mutex_lock(&(test->print_mutex)) != 0) {
+ perror("iperf_errexit: pthread_mutex_lock");
+ }
+
if (test && test->outfile && test->outfile != stdout) {
if (ct) {
fprintf(test->outfile, "%s", ct);
@@ -112,13 +145,20 @@ iperf_errexit(struct iperf_test *test, const char *format, ...)
}
fprintf(stderr, "iperf3: %s\n", str);
}
+
+ if (test != NULL && pthread_mutex_unlock(&(test->print_mutex)) != 0) {
+ perror("iperf_errexit: pthread_mutex_unlock");
+ }
+ }
+
va_end(argp);
if (test)
iperf_delete_pidfile(test);
- exit(1);
+ exit(exit_code);
}
-int i_errno;
+int i_errno = 0;
+const char *errarg = NULL;
char *
iperf_strerror(int int_errno)
@@ -147,7 +187,7 @@ iperf_strerror(int int_errno)
snprintf(errstr, len, "some option you are trying to set is client only");
break;
case IEDURATION:
- snprintf(errstr, len, "test duration too long (maximum = %d seconds)", MAX_TIME);
+ snprintf(errstr, len, "test duration valid values are 0 to %d seconds", MAX_TIME);
break;
case IENUMSTREAMS:
snprintf(errstr, len, "number of parallel streams too large (maximum = %d)", MAX_STREAMS);
@@ -161,7 +201,7 @@ iperf_strerror(int int_errno)
case IEINTERVAL:
snprintf(errstr, len, "invalid report interval (min = %g, max = %g seconds)", MIN_INTERVAL, MAX_INTERVAL);
break;
- case IEBIND: /* UNUSED */
+ case IEBIND: /* UNUSED */
snprintf(errstr, len, "--bind must be specified to use --cport");
break;
case IEUDPBLOCKSIZE:
@@ -176,12 +216,15 @@ iperf_strerror(int int_errno)
case IESETSERVERAUTH:
snprintf(errstr, len, "you must specify a path to a valid RSA private key and a user credential file");
break;
- case IEBADFORMAT:
- snprintf(errstr, len, "bad format specifier (valid formats are in the set [kmgtKMGT])");
- break;
- case IEBADPORT:
- snprintf(errstr, len, "port number must be between 1 and 65535 inclusive");
- break;
+ case IESERVERAUTHUSERS:
+ snprintf(errstr, len, "cannot access authorized users file");
+ break;
+ case IEBADFORMAT:
+ snprintf(errstr, len, "bad format specifier (valid formats are in the set [kmgtKMGT])");
+ break;
+ case IEBADPORT:
+ snprintf(errstr, len, "port number must be between 1 and 65535 inclusive");
+ break;
case IEMSS:
snprintf(errstr, len, "TCP MSS too large (maximum = %d bytes)", MAX_MSS);
break;
@@ -189,7 +232,7 @@ iperf_strerror(int int_errno)
snprintf(errstr, len, "this OS does not support sendfile");
break;
case IEOMIT:
- snprintf(errstr, len, "bogus value for --omit");
+ snprintf(errstr, len, "bogus value for --omit (maximum = %d seconds)", MAX_OMIT_TIME);
break;
case IEUNIMP:
snprintf(errstr, len, "an option you are trying to set is not implemented yet");
@@ -204,13 +247,13 @@ iperf_strerror(int int_errno)
case IEENDCONDITIONS:
snprintf(errstr, len, "only one test end condition (-t, -n, -k) may be specified");
break;
- case IELOGFILE:
- snprintf(errstr, len, "unable to open log file");
- perr = 1;
- break;
- case IENOSCTP:
- snprintf(errstr, len, "no SCTP support available");
- break;
+ case IELOGFILE:
+ snprintf(errstr, len, "unable to open log file");
+ perr = 1;
+ break;
+ case IENOSCTP:
+ snprintf(errstr, len, "no SCTP support available");
+ break;
case IENEWTEST:
snprintf(errstr, len, "unable to create a new test");
perr = 1;
@@ -224,13 +267,13 @@ iperf_strerror(int int_errno)
break;
case IELISTEN:
snprintf(errstr, len, "unable to start listener for connections");
- herr = 1;
+ herr = 1;
perr = 1;
break;
case IECONNECT:
snprintf(errstr, len, "unable to connect to server - server may have stopped running or use a different port, firewall issue, etc.");
perr = 1;
- herr = 1;
+ herr = 1;
break;
case IEACCEPT:
snprintf(errstr, len, "unable to accept connection from client");
@@ -353,14 +396,17 @@ iperf_strerror(int int_errno)
case IEUDPFILETRANSFER:
snprintf(errstr, len, "cannot transfer file using UDP");
break;
+ case IEUNITVAL:
+ snprintf(errstr, len, "invalid unit value or suffix: '%s'", errarg);
+ break;
case IERVRSONLYRCVTIMEOUT:
snprintf(errstr, len, "client receive timeout is valid only in receiving mode");
perr = 1;
break;
- case IEDAEMON:
- snprintf(errstr, len, "unable to become a daemon");
- perr = 1;
- break;
+ case IEDAEMON:
+ snprintf(errstr, len, "unable to become a daemon");
+ perr = 1;
+ break;
case IECREATESTREAM:
snprintf(errstr, len, "unable to create a new stream");
herr = 1;
@@ -373,7 +419,7 @@ iperf_strerror(int int_errno)
break;
case IESTREAMLISTEN:
snprintf(errstr, len, "unable to start stream listener");
- herr = 1;
+ herr = 1;
perr = 1;
break;
case IESTREAMCONNECT:
@@ -411,14 +457,14 @@ iperf_strerror(int int_errno)
snprintf(errstr, len, "unable to set TCP_CONGESTION: "
"Supplied congestion control algorithm not supported on this host");
break;
- case IEPIDFILE:
+ case IEPIDFILE:
snprintf(errstr, len, "unable to write PID file");
perr = 1;
break;
- case IEV6ONLY:
- snprintf(errstr, len, "Unable to set/reset IPV6_V6ONLY");
- perr = 1;
- break;
+ case IEV6ONLY:
+ snprintf(errstr, len, "Unable to set/reset IPV6_V6ONLY");
+ perr = 1;
+ break;
case IESETSCTPDISABLEFRAG:
snprintf(errstr, len, "unable to set SCTP_DISABLE_FRAGMENTS");
perr = 1;
@@ -427,48 +473,100 @@ iperf_strerror(int int_errno)
snprintf(errstr, len, "unable to set SCTP_INIT num of SCTP streams\n");
perr = 1;
break;
- case IESETPACING:
- snprintf(errstr, len, "unable to set socket pacing");
- perr = 1;
- break;
- case IESETBUF2:
- snprintf(errstr, len, "socket buffer size not set correctly");
- break;
- case IEREVERSEBIDIR:
- snprintf(errstr, len, "cannot be both reverse and bidirectional");
+ case IESETPACING:
+ snprintf(errstr, len, "unable to set socket pacing");
+ perr = 1;
+ break;
+ case IESETBUF2:
+ snprintf(errstr, len, "socket buffer size not set correctly");
+ break;
+ 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;
+ case IETOTALINTERVAL:
+ snprintf(errstr, len, "invalid time interval for calculating average data rate");
break;
- case IETOTALRATE:
- snprintf(errstr, len, "total required bandwidth is larger than server limit");
+ case IESKEWTHRESHOLD:
+ snprintf(errstr, len, "skew threshold must be a positive number");
break;
- case IESKEWTHRESHOLD:
- snprintf(errstr, len, "skew threshold must be a positive number");
+ case IEIDLETIMEOUT:
+ snprintf(errstr, len, "idle timeout parameter is not positive or larger than allowed limit");
break;
- case IEIDLETIMEOUT:
- snprintf(errstr, len, "idle timeout parameter is not positive or larger than allowed limit");
+ case IEBINDDEV:
+ snprintf(errstr, len, "Unable to bind-to-device (check perror, maybe permissions?)");
break;
- case IEBINDDEV:
- snprintf(errstr, len, "Unable to bind-to-device (check perror, maybe permissions?)");
+ case IEBINDDEVNOSUPPORT:
+ snprintf(errstr, len, "`%%` is not supported as system does not support bind to device");
break;
- case IEBINDDEVNOSUPPORT:
- snprintf(errstr, len, "`%%` is not supported as system does not support bind to device");
+ case IEHOSTDEV:
+ snprintf(errstr, len, "host device name (ip%%) is supported (and required) only for IPv6 link-local address");
break;
- case IEHOSTDEV:
- snprintf(errstr, len, "host device name (ip%%) is supported (and required) only for IPv6 link-local address");
- break;
- case IENOMSG:
- snprintf(errstr, len, "idle timeout for receiving data");
+ case IENOMSG:
+ snprintf(errstr, len, "idle timeout for receiving data");
break;
- case IESETDONTFRAGMENT:
- snprintf(errstr, len, "unable to set IP Do-Not-Fragment flag");
+ case IESETDONTFRAGMENT:
+ snprintf(errstr, len, "unable to set IP Do-Not-Fragment flag");
break;
case IESETUSERTIMEOUT:
snprintf(errstr, len, "unable to set TCP USER_TIMEOUT");
perr = 1;
break;
- default:
- snprintf(errstr, len, "int_errno=%d", int_errno);
- perr = 1;
- break;
+ case IEPTHREADCREATE:
+ snprintf(errstr, len, "unable to create thread");
+ perr = 1;
+ break;
+ case IEPTHREADCANCEL:
+ snprintf(errstr, len, "unable to cancel thread");
+ perr = 1;
+ break;
+ case IEPTHREADJOIN:
+ snprintf(errstr, len, "unable to join thread");
+ perr = 1;
+ break;
+ case IEPTHREADATTRINIT:
+ snprintf(errstr, len, "unable to create thread attributes");
+ perr = 1;
+ break;
+ case IEPTHREADSIGMASK:
+ snprintf(errstr, len, "unable to change mask of blocked signals");
+ break;
+ case IEPTHREADATTRDESTROY:
+ snprintf(errstr, len, "unable to destroy thread attributes");
+ break;
+ case IECNTLKA:
+ snprintf(errstr, len, "control connection Keepalive period should be larger than the full retry period (interval * count)");
+ perr = 1;
+ break;
+ case IESETCNTLKA:
+ snprintf(errstr, len, "unable to set socket keepalive (SO_KEEPALIVE) option");
+ perr = 1;
+ break;
+ case IESETCNTLKAKEEPIDLE:
+ snprintf(errstr, len, "unable to set socket keepalive TCP period (TCP_KEEPIDLE) option");
+ perr = 1;
+ break;
+ case IESETCNTLKAINTERVAL:
+ snprintf(errstr, len, "unable to set/get socket keepalive TCP retry interval (TCP_KEEPINTVL) option");
+ perr = 1;
+ break;
+ case IESETCNTLKACOUNT:
+ snprintf(errstr, len, "unable to set/get socket keepalive TCP number of retries (TCP_KEEPCNT) option");
+ perr = 1;
+ break;
+ case IEMAXSERVERTESTDURATIONEXCEEDED:
+ snprintf(errstr, len, "client's requested duration exceeds the server's maximum permitted limit");
+ break;
+ case IESERVERTESTDURATIONEXPIRED:
+ snprintf(errstr, len, "server test duration expired");
+ perr = 1;
+ 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 62e3bc441..eb07c9651 100644
--- a/src/iperf_locale.c
+++ b/src/iperf_locale.c
@@ -59,7 +59,7 @@
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE CONTIBUTORS OR COPYRIGHT
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE
@@ -81,6 +81,8 @@
#include "version.h"
+#include
+
#ifdef __cplusplus
extern "C"
{
@@ -103,17 +105,20 @@ const char usage_longstr[] = "Usage: iperf3 [-s|-c host] [options]\n"
" -I, --pidfile file write PID file\n"
" -F, --file name xmit/recv the specified file\n"
#if defined(HAVE_CPU_AFFINITY)
- " -A, --affinity n/n,m set CPU affinity\n"
+ " -A, --affinity n[,m] set CPU affinity core number to n (the core the process will use)\n"
+ " (optional Client only m - the Server's core number for this test)\n"
#endif /* HAVE_CPU_AFFINITY */
-#if defined(HAVE_SO_BINDTODEVICE)
+#if defined(CAN_BIND_TO_DEVICE)
" -B, --bind [%%] bind to the interface associated with the address \n"
" (optional equivalent to `--bind-dev `)\n"
" --bind-dev bind to the network interface with SO_BINDTODEVICE\n"
-#else /* HAVE_SO_BINDTODEVICE */
+#else /* CAN_BIND_TO_DEVICE */
" -B, --bind bind to the interface associated with the address \n"
-#endif /* HAVE_SO_BINDTODEVICE */
+#endif /* CAN_BIND_TO_DEVICE */
" -V, --verbose more detailed output\n"
" -J, --json output in JSON format\n"
+ " --json-stream output in line-delimited JSON format\n"
+ " --json-stream-full-output output in JSON format with JSON streams enabled\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"
@@ -124,8 +129,15 @@ const char usage_longstr[] = "Usage: iperf3 [-s|-c host] [options]\n"
" --snd-timeout # timeout for unacknowledged TCP data\n"
" (in ms, default is system settings)\n"
#endif /* HAVE_TCP_USER_TIMEOUT */
+#if defined(HAVE_TCP_KEEPALIVE)
+ " --cntl-ka[=#/#/#] use control connection TCP keepalive - KEEPIDLE/KEEPINTV/KEEPCNT\n"
+ " each value is optional with system settings default\n"
+#endif //HAVE_TCP_KEEPALIVE
+#if defined(HAVE_IPPROTO_MPTCP)
+ " -m, --mptcp use MPTCP rather than plain TCP\n"
+#endif
" -d, --debug[=#] emit debugging output\n"
- " (optional optional \"=\" and debug level: 1-4. Default is 4 - all messages)\n"
+ " (optional \"=\" and debug level: 1-4. Default is 4 - all messages)\n"
" -v, --version show version information and quit\n"
" -h, --help show this message and quit\n"
"Server specific:\n"
@@ -137,6 +149,7 @@ const char usage_longstr[] = "Usage: iperf3 [-s|-c host] [options]\n"
" total data rate. Default is 5 seconds)\n"
" --idle-timeout # restart idle server after # seconds in case it\n"
" got stuck (default - no timeout)\n"
+ " --server-max-duration # max time, in seconds, that an iperf test can run against the server\n"
#if defined(HAVE_SSL)
" --rsa-private-key-path path to the RSA private key used to decrypt\n"
" authentication credentials\n"
@@ -144,6 +157,7 @@ const char usage_longstr[] = "Usage: iperf3 [-s|-c host] [options]\n"
" credentials\n"
" --time-skew-threshold time skew threshold (in seconds) between the server\n"
" and client during the authentication process\n"
+ " --use-pkcs1-padding use pkcs1 padding at your own risk\n"
#endif //HAVE_SSL
"Client specific:\n"
" -c, --client [%%] run in client mode, connecting to \n"
@@ -158,14 +172,17 @@ const char usage_longstr[] = "Usage: iperf3 [-s|-c host] [options]\n"
" -b, --bitrate #[KMG][/#] target bitrate in bits/sec (0 for unlimited)\n"
" (default %d Mbit/sec for UDP, unlimited for TCP)\n"
" (optional slash and packet count for burst mode)\n"
- " --pacing-timer #[KMG] set the timing for pacing, in microseconds (default %d)\n"
+ " --pacing-timer #[KMG] set the Server timing for pacing, in microseconds (default %d)\n"
+ " (deprecated - for servers using older versions backward compatibility)\n"
#if defined(HAVE_SO_MAX_PACING_RATE)
" --fq-rate #[KMG] enable fair-queuing based socket pacing in\n"
" bits/sec (Linux only)\n"
#endif
" -t, --time # time in seconds to transmit for (default %d secs)\n"
- " -n, --bytes #[KMG] number of bytes to transmit (instead of -t)\n"
- " -k, --blockcount #[KMG] number of blocks (packets) to transmit (instead of -t or -n)\n"
+ " -n, --bytes #[KMG] transmit until the end of the interval when the client sent or received\n"
+ " (per direction) at least this number of bytes (instead of -t or -k)\n"
+ " -k, --blockcount #[KMG] transmit until the end of the interval when the client sent or received\n"
+ " (per direction) at least this number of blocks (instead of -t or -n)\n"
" -l, --length #[KMG] length of buffer to read or write\n"
" (default %d KB for TCP, dynamic or %d for UDP)\n"
" --cport bind to a specific client port (TCP and UDP, default: ephemeral port)\n"
@@ -194,11 +211,15 @@ const char usage_longstr[] = "Usage: iperf3 [-s|-c host] [options]\n"
" -L, --flowlabel N set the IPv6 flow label (only supported on Linux)\n"
#endif /* HAVE_FLOWLABEL */
" -Z, --zerocopy use a 'zero copy' method of sending data\n"
+#if defined(HAVE_MSG_TRUNC)
+ " --skip-rx-copy ignore received messages using MSG_TRUNC option\n"
+#endif /* HAVE_MSG_TRUNC */
" -O, --omit N perform pre-test for N seconds and omit the pre-test statistics\n"
" -T, --title str prefix every output line with this string\n"
" --extra-data str data string to include in client and server JSON\n"
" --get-server-output get results from server\n"
" --udp-counters-64bit use 64-bit counters in UDP test packets\n"
+ " --gsro enable UDP GSO/GRO on both client and server (client-only option)\n"
" --repeating-payload use repeating pattern in payload, instead of\n"
" randomized payload (like in iperf2)\n"
#if defined(HAVE_DONT_FRAGMENT)
@@ -287,10 +308,10 @@ const char test_start_time[] =
"Starting Test: protocol: %s, %d streams, %d byte blocks, omitting %d seconds, %d second test, tos %d\n";
const char test_start_bytes[] =
-"Starting Test: protocol: %s, %d streams, %d byte blocks, omitting %d seconds, %llu bytes to send, tos %d\n";
+"Starting Test: protocol: %s, %d streams, %d byte blocks, omitting %d seconds, %"PRIuFAST64" bytes to send, tos %d\n";
const char test_start_blocks[] =
-"Starting Test: protocol: %s, %d streams, %d byte blocks, omitting %d seconds, %d blocks to send, tos %d\n";
+"Starting Test: protocol: %s, %d streams, %d byte blocks, omitting %d seconds, %"PRIuFAST64" blocks to send, tos %d\n";
/* -------------------------------------------------------------------
@@ -304,10 +325,10 @@ const char report_connecting[] =
"Connecting to host %s, port %d\n";
const char report_authentication_succeeded[] =
-"Authentication succeeded for user '%s' ts %ld\n";
+"Authentication succeeded for user '%s' ts %" PRIu64 "\n";
const char report_authentication_failed[] =
-"Authentication failed with return code %d for user '%s' ts %ld\n";
+"Authentication failed with return code %d for user '%s' ts %" PRIu64 "\n";
const char report_reverse[] =
"Reverse mode, remote host %s is sending\n";
@@ -376,16 +397,19 @@ const char report_bw_format[] =
"[%3d]%s %6.2f-%-6.2f sec %ss %ss/sec %s\n";
const char report_bw_retrans_format[] =
-"[%3d]%s %6.2f-%-6.2f sec %ss %ss/sec %3u %s\n";
+"[%3d]%s %6.2f-%-6.2f sec %ss %ss/sec %3ld %s\n";
const char report_bw_retrans_cwnd_format[] =
-"[%3d]%s %6.2f-%-6.2f sec %ss %ss/sec %3u %ss %s\n";
+"[%3d]%s %6.2f-%-6.2f sec %ss %ss/sec %3ld %ss %s\n";
const char report_bw_udp_format[] =
-"[%3d]%s %6.2f-%-6.2f sec %ss %ss/sec %5.3f ms %d/%d (%.2g%%) %s\n";
+"[%3d]%s %6.2f-%-6.2f sec %ss %ss/sec %5.3f ms %" PRId64 "/%" PRId64 " (%.2g%%) %s\n";
+
+const char report_bw_udp_format_no_omitted_error[] =
+"[%3d]%s %6.2f-%-6.2f sec %ss %ss/sec %5.3f ms Unknown/%" PRId64 " %s\n";
const char report_bw_udp_sender_format[] =
-"[%3d]%s %6.2f-%-6.2f sec %ss %ss/sec %s %d %s\n";
+"[%3d]%s %6.2f-%-6.2f sec %ss %ss/sec %s %" PRId64 " %s\n";
const char report_summary[] =
"Test Complete. Summary Results:\n";
@@ -394,13 +418,13 @@ const char report_sum_bw_format[] =
"[SUM]%s %6.2f-%-6.2f sec %ss %ss/sec %s\n";
const char report_sum_bw_retrans_format[] =
-"[SUM]%s %6.2f-%-6.2f sec %ss %ss/sec %3d %s\n";
+"[SUM]%s %6.2f-%-6.2f sec %ss %ss/sec %3"PRId64" %s\n";
const char report_sum_bw_udp_format[] =
-"[SUM]%s %6.2f-%-6.2f sec %ss %ss/sec %5.3f ms %d/%d (%.2g%%) %s\n";
+"[SUM]%s %6.2f-%-6.2f sec %ss %ss/sec %5.3f ms %" PRId64 "/%" PRId64 " (%.2g%%) %s\n";
const char report_sum_bw_udp_sender_format[] =
-"[SUM]%s %6.2f-%-6.2f sec %ss %ss/sec %s %d %s\n";
+"[SUM]%s %6.2f-%-6.2f sec %ss %ss/sec %s %" PRId64 " %s\n";
const char report_omitted[] = "(omitted)";
@@ -411,7 +435,7 @@ const char report_outoforder[] =
"[%3d]%s %4.1f-%4.1f sec %d datagrams received out-of-order\n";
const char report_sum_outoforder[] =
-"[SUM]%s %4.1f-%4.1f sec %d datagrams received out-of-order\n";
+"[SUM]%s %4.1f-%4.1f sec %"PRIu64" datagrams received out-of-order\n";
const char report_peer[] =
"[%3d] local %s port %u connected with %s port %u\n";
diff --git a/src/iperf_locale.h b/src/iperf_locale.h
index 47cf416ae..bc9c96cb4 100644
--- a/src/iperf_locale.h
+++ b/src/iperf_locale.h
@@ -78,6 +78,7 @@ extern const char report_bw_format[] ;
extern const char report_bw_retrans_format[] ;
extern const char report_bw_retrans_cwnd_format[] ;
extern const char report_bw_udp_format[] ;
+extern const char report_bw_udp_format_no_omitted_error[] ;
extern const char report_bw_udp_sender_format[] ;
extern const char report_summary[] ;
extern const char report_sum_bw_format[] ;
diff --git a/src/iperf_pthread.c b/src/iperf_pthread.c
new file mode 100644
index 000000000..9798a4112
--- /dev/null
+++ b/src/iperf_pthread.c
@@ -0,0 +1,41 @@
+#include "iperf_config.h"
+
+#if defined(HAVE_PTHREAD) && defined(__ANDROID__)
+
+/* Workaround for `pthread_cancel()` in Android, using `pthread_kill()` instead,
+ * as Android NDK does not support `pthread_cancel()`.
+ */
+
+#include
+#include
+#include "iperf_pthread.h"
+
+void iperf_thread_exit_handler(int sig)
+{
+ pthread_exit(0);
+}
+
+int iperf_set_thread_exit_handler() {
+ int rc;
+ struct sigaction actions;
+
+ memset(&actions, 0, sizeof(actions));
+ sigemptyset(&actions.sa_mask);
+ actions.sa_flags = 0;
+ actions.sa_handler = iperf_thread_exit_handler;
+
+ rc = sigaction(SIGUSR1, &actions, NULL);
+ return rc;
+}
+
+int pthread_setcanceltype(int type, int *oldtype) { return 0; }
+int pthread_setcancelstate(int state, int *oldstate) { return 0; }
+int pthread_cancel(pthread_t thread_id) {
+ int status;
+ if ((status = iperf_set_thread_exit_handler()) == 0) {
+ status = pthread_kill(thread_id, SIGUSR1);
+ }
+ return status;
+}
+
+#endif // defined(HAVE_PTHREAD) && defined(__ANDROID__)
diff --git a/src/iperf_pthread.h b/src/iperf_pthread.h
new file mode 100644
index 000000000..021ad3890
--- /dev/null
+++ b/src/iperf_pthread.h
@@ -0,0 +1,33 @@
+#include "iperf_config.h"
+
+#if defined(HAVE_PTHREAD)
+
+#include
+
+#if defined(__ANDROID__)
+
+/* Adding missing `pthread` related definitions in Android.
+ */
+
+ enum
+ {
+ PTHREAD_CANCEL_ENABLE,
+ #define PTHREAD_CANCEL_ENABLE PTHREAD_CANCEL_ENABLE
+ PTHREAD_CANCEL_DISABLE
+ #define PTHREAD_CANCEL_DISABLE PTHREAD_CANCEL_DISABLE
+ };
+ enum
+ {
+ PTHREAD_CANCEL_DEFERRED,
+ #define PTHREAD_CANCEL_DEFERRED PTHREAD_CANCEL_DEFERRED
+ PTHREAD_CANCEL_ASYNCHRONOUS
+ #define PTHREAD_CANCEL_ASYNCHRONOUS PTHREAD_CANCEL_ASYNCHRONOUS
+ };
+
+int pthread_setcanceltype(int type, int *oldtype);
+int pthread_setcancelstate(int state, int *oldstate);
+int pthread_cancel(pthread_t thread_id);
+
+#endif // defined(__ANDROID__)
+
+#endif // defined(HAVE_PTHREAD)
diff --git a/src/iperf_sctp.c b/src/iperf_sctp.c
index 104083281..1e02a8d50 100644
--- a/src/iperf_sctp.c
+++ b/src/iperf_sctp.c
@@ -39,10 +39,6 @@
#include
#include
-#ifdef HAVE_NETINET_SCTP_H
-#include
-#endif /* HAVE_NETINET_SCTP_H */
-
#include "iperf.h"
#include "iperf_api.h"
#include "iperf_sctp.h"
@@ -60,7 +56,7 @@ iperf_sctp_recv(struct iperf_stream *sp)
#if defined(HAVE_SCTP_H)
int r;
- r = Nread(sp->socket, sp->buffer, sp->settings->blksize, Psctp);
+ r = Nread_no_select(sp->socket, sp->buffer, sp->settings->blksize, Psctp);
if (r < 0)
return r;
@@ -172,7 +168,7 @@ iperf_sctp_listen(struct iperf_test *test)
/*
* If binding to the wildcard address with no explicit address
* family specified, then force us to get an AF_INET6 socket.
- * More details in the comments in netanounce().
+ * More details in the comments in netannounce().
*/
if (test->settings->domain == AF_UNSPEC && !test->bind_address) {
hints.ai_family = AF_INET6;
@@ -213,11 +209,7 @@ iperf_sctp_listen(struct iperf_test *test)
}
if (test->bind_dev) {
-#if defined(SO_BINDTODEVICE)
- if (setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE,
- test->bind_dev, IFNAMSIZ) < 0)
-#endif // SO_BINDTODEVICE
- {
+ if (bind_to_device(s, res->ai_family, test->bind_dev) < 0) {
saved_errno = errno;
close(s);
freeaddrinfo(res);
@@ -352,11 +344,7 @@ iperf_sctp_connect(struct iperf_test *test)
}
if (test->bind_dev) {
-#if defined(SO_BINDTODEVICE)
- if (setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE,
- test->bind_dev, IFNAMSIZ) < 0)
-#endif // SO_BINDTODEVICE
- {
+ if (bind_to_device(s, server_res->ai_family, test->bind_dev) < 0) {
saved_errno = errno;
close(s);
freeaddrinfo(local_res);
@@ -734,3 +722,49 @@ iperf_sctp_bindx(struct iperf_test *test, int s, int is_server)
return -1;
#endif /* HAVE_SCTP_H */
}
+
+
+/* iperf_sctp_get_rtt
+ *
+ * Get SCTP stream RTT.
+ * Assuming that iperf3 supports only one-to-one SCTP association, and not one-to-many association.
+ *
+ * Main resources used are RFC-6458, man pages for SCTP,
+ * https://docs.oracle.com/cd/E19253-01/817-4415/sockets-199/index.html.
+ *
+ */
+int
+iperf_sctp_get_info(struct iperf_stream *sp, struct iperf_sctp_info *sctp_info)
+{
+#if defined(HAVE_SCTP_H)
+ struct sctp_status status;
+ socklen_t len;
+ sctp_assoc_t assoc_id;
+ int rc = 0;
+
+ if (sp->test->protocol->id != Psctp) {
+ rc = -1;
+ } else {
+#ifdef SCTP_FUTURE_ASSOC
+ assoc_id = SCTP_FUTURE_ASSOC;
+#else
+ assoc_id = 0;
+#endif
+ len = sizeof(status);
+ rc = sctp_opt_info(sp->socket, assoc_id, SCTP_STATUS, &status, &len);
+ if (rc < 0) {
+ if (sp->test->debug_level >= DEBUG_LEVEL_ERROR)
+ iperf_err(sp->test, "sctp_opt_info get SCTP_STATUS for socket %d failed with errno %d - %s", sp->socket, errno, strerror(errno));
+ } else {
+ sctp_info->wnd = status.sstat_rwnd;
+ sctp_info->rtt = status.sstat_primary.spinfo_srtt;
+ sctp_info->pmtu = status.sstat_primary.spinfo_mtu;
+ sctp_info->cwnd = status.sstat_primary.spinfo_cwnd;
+ }
+ }
+
+ return rc;
+#else
+ return -1;
+#endif /* HAVE_SCTP_H */
+}
diff --git a/src/iperf_sctp.h b/src/iperf_sctp.h
index 764c410df..2956266f2 100644
--- a/src/iperf_sctp.h
+++ b/src/iperf_sctp.h
@@ -27,6 +27,10 @@
#ifndef IPERF_SCTP_H
#define IPERF_SCTP_H
+#ifdef HAVE_NETINET_SCTP_H
+#include
+#endif /* HAVE_NETINET_SCTP_H */
+
/**
* iperf_sctp_accept -- accepts a new SCTP connection
* on sctp_listener_socket for SCTP data and param/result
@@ -47,7 +51,7 @@ int iperf_sctp_recv(struct iperf_stream *);
/**
* iperf_sctp_send -- sends the client data for sctp
- * and the Param/result message exchanges
+ * and the Param/result message exchanges
* returns: bytes sent
*
*/
@@ -65,4 +69,6 @@ int iperf_sctp_init(struct iperf_test *test);
int iperf_sctp_bindx(struct iperf_test *test, int s, int is_server);
+int iperf_sctp_get_info(struct iperf_stream *sp, struct iperf_sctp_info *sctp_info);
+
#endif
diff --git a/src/iperf_server_api.c b/src/iperf_server_api.c
index 18f105ded..66401fa6b 100644
--- a/src/iperf_server_api.c
+++ b/src/iperf_server_api.c
@@ -1,5 +1,5 @@
/*
- * iperf, Copyright (c) 2014-2022 The Regents of the University of
+ * iperf, Copyright (c) 2014-2023 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.
@@ -40,13 +40,12 @@
#include
#include
#include
-#ifdef HAVE_STDINT_H
#include
-#endif
#include
#include
#include
#include
+#include
#include "iperf.h"
#include "iperf_api.h"
@@ -66,6 +65,50 @@
#endif /* TCP_CA_NAME_MAX */
#endif /* HAVE_TCP_CONGESTION */
+void *
+iperf_server_worker_run(void *s) {
+ struct iperf_stream *sp = (struct iperf_stream *) s;
+ struct iperf_test *test = sp->test;
+
+ /* Blocking signal to make sure that signal will be handled by main thread */
+ sigset_t set;
+ sigemptyset(&set);
+#ifdef SIGTERM
+ sigaddset(&set, SIGTERM);
+#endif
+#ifdef SIGHUP
+ sigaddset(&set, SIGHUP);
+#endif
+#ifdef SIGINT
+ sigaddset(&set, SIGINT);
+#endif
+ if (pthread_sigmask(SIG_BLOCK, &set, NULL) != 0) {
+ i_errno = IEPTHREADSIGMASK;
+ goto cleanup_and_fail;
+ }
+
+ /* Allow this thread to be cancelled even if it's in a syscall */
+ pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
+ pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
+
+ while (! (test->done) && ! (sp->done)) {
+ if (sp->sender) {
+ if (iperf_send_mt(sp) < 0) {
+ goto cleanup_and_fail;
+ }
+ }
+ else {
+ if (iperf_recv_mt(sp) < 0) {
+ goto cleanup_and_fail;
+ }
+ }
+ }
+ return NULL;
+
+ cleanup_and_fail:
+ return NULL;
+}
+
int
iperf_server_listen(struct iperf_test *test)
{
@@ -110,6 +153,7 @@ int
iperf_accept(struct iperf_test *test)
{
int s;
+ int ret = -1;
signed char rbuf = ACCESS_DENIED;
socklen_t len;
struct sockaddr_storage addr;
@@ -117,7 +161,7 @@ iperf_accept(struct iperf_test *test)
len = sizeof(addr);
if ((s = accept(test->listener, (struct sockaddr *) &addr, &len)) < 0) {
i_errno = IEACCEPT;
- return -1;
+ return ret;
}
if (test->ctrl_sck == -1) {
@@ -127,7 +171,7 @@ iperf_accept(struct iperf_test *test)
int flag = 1;
if (setsockopt(test->ctrl_sck, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int))) {
i_errno = IESETNODELAY;
- return -1;
+ goto error_handling;
}
#if defined(HAVE_TCP_USER_TIMEOUT)
@@ -135,34 +179,46 @@ iperf_accept(struct iperf_test *test)
if ((opt = test->settings->snd_timeout)) {
if (setsockopt(s, IPPROTO_TCP, TCP_USER_TIMEOUT, &opt, sizeof(opt)) < 0) {
i_errno = IESETUSERTIMEOUT;
- return -1;
+ goto error_handling;
}
}
#endif /* HAVE_TCP_USER_TIMEOUT */
- if (Nread(test->ctrl_sck, test->cookie, COOKIE_SIZE, Ptcp) < 0) {
- i_errno = IERECVCOOKIE;
+#if defined (HAVE_TCP_KEEPALIVE)
+ // Set Control Connection TCP Keepalive (especially useful for long UDP test sessions)
+ if (iperf_set_control_keepalive(test) < 0)
return -1;
+#endif //HAVE_TCP_KEEPALIVE
+
+ if (Nread(test->ctrl_sck, test->cookie, COOKIE_SIZE, Ptcp) != COOKIE_SIZE) {
+ /*
+ * Note this error covers both the case of a system error
+ * or the inability to read the correct amount of data
+ * (i.e. timed out).
+ */
+ i_errno = IERECVCOOKIE;
+ goto error_handling;
}
- FD_SET(test->ctrl_sck, &test->read_set);
- if (test->ctrl_sck > test->max_fd) test->max_fd = test->ctrl_sck;
+ FD_SET(test->ctrl_sck, &test->read_set);
+ if (test->ctrl_sck > test->max_fd) test->max_fd = test->ctrl_sck;
- if (iperf_set_send_state(test, PARAM_EXCHANGE) != 0)
- return -1;
+ if (iperf_set_send_state(test, PARAM_EXCHANGE) != 0)
+ goto error_handling;
if (iperf_exchange_parameters(test) < 0)
- return -1;
- if (test->server_affinity != -1)
- if (iperf_setaffinity(test, test->server_affinity) != 0)
- return -1;
+ goto error_handling;
+ if (test->server_affinity != -1) {
+ if (iperf_setaffinity(test, test->server_affinity) != 0)
+ goto error_handling;
+ }
if (test->on_connect)
test->on_connect(test);
} else {
- /*
- * Don't try to read from the socket. It could block an ongoing test.
- * Just send ACCESS_DENIED.
+ /*
+ * Don't try to read from the socket. It could block an ongoing test.
+ * Just send ACCESS_DENIED.
* Also, if sending failed, don't return an error, as the request is not related
* to the ongoing test, and returning an error will terminate the test.
- */
+ */
if (Nwrite(s, (char*) &rbuf, sizeof(rbuf), Ptcp) < 0) {
if (test->debug)
printf("failed to send ACCESS_DENIED to an unsolicited connection request during active test\n");
@@ -172,8 +228,9 @@ iperf_accept(struct iperf_test *test)
}
close(s);
}
-
return 0;
+ error_handling:
+ return ret;
}
@@ -184,12 +241,16 @@ iperf_handle_message_server(struct iperf_test *test)
int rval;
struct iperf_stream *sp;
+ if (test->debug_level >= DEBUG_LEVEL_INFO) {
+ iperf_printf(test, "Reading new State from the Client - current state is %d-%s\n", test->state, state_to_text(test->state));
+ }
+
// XXX: Need to rethink how this behaves to fit API
if ((rval = Nread(test->ctrl_sck, (char*) &test->state, sizeof(signed char), Ptcp)) <= 0) {
if (rval == 0) {
- iperf_err(test, "the client has unexpectedly closed the connection");
+ iperf_err(test, "the client has unexpectedly closed the connection");
i_errno = IECTRLCLOSE;
- test->state = IPERF_DONE;
+ iperf_set_test_state(test, IPERF_DONE);
return 0;
} else {
i_errno = IERECVMESSAGE;
@@ -197,11 +258,15 @@ iperf_handle_message_server(struct iperf_test *test)
}
}
+ if (test->debug_level >= DEBUG_LEVEL_INFO) {
+ iperf_printf(test, "State change: server received and changed State to %d-%s\n", test->state, state_to_text(test->state));
+ }
+
switch(test->state) {
case TEST_START:
break;
case TEST_END:
- test->done = 1;
+ test->done = 1;
cpu_util(test->cpu_util);
test->stats_callback(test);
SLIST_FOREACH(sp, &test->streams, streams) {
@@ -210,11 +275,11 @@ iperf_handle_message_server(struct iperf_test *test)
close(sp->socket);
}
test->reporter_callback(test);
- if (iperf_set_send_state(test, EXCHANGE_RESULTS) != 0)
+ if (iperf_set_send_state(test, EXCHANGE_RESULTS) != 0)
return -1;
if (iperf_exchange_results(test) < 0)
return -1;
- if (iperf_set_send_state(test, DISPLAY_RESULTS) != 0)
+ if (iperf_set_send_state(test, DISPLAY_RESULTS) != 0)
return -1;
if (test->on_test_finish)
test->on_test_finish(test);
@@ -239,7 +304,7 @@ iperf_handle_message_server(struct iperf_test *test)
FD_CLR(sp->socket, &test->write_set);
close(sp->socket);
}
- test->state = IPERF_DONE;
+ iperf_set_test_state(test, IPERF_DONE);
break;
default:
i_errno = IEMESSAGE;
@@ -254,17 +319,30 @@ server_timer_proc(TimerClientData client_data, struct iperf_time *nowP)
{
struct iperf_test *test = client_data.p;
struct iperf_stream *sp;
+ int32_t err;
test->timer = NULL;
if (test->done)
return;
test->done = 1;
+ iperf_err(test, "error - server test duration expired - test is terminated by the server");
+ if (iperf_set_send_state(test, SERVER_ERROR) == 0) {
+ i_errno = IESERVERTESTDURATIONEXPIRED;
+ err = htonl(i_errno);
+ if (Nwrite(test->ctrl_sck, (char*) &err, sizeof(err), Ptcp) == sizeof(err)) {
+ err = 0;
+ Nwrite(test->ctrl_sck, (char*) &err, sizeof(err), Ptcp);
+ };
+ }
/* Free streams */
while (!SLIST_EMPTY(&test->streams)) {
sp = SLIST_FIRST(&test->streams);
- SLIST_REMOVE_HEAD(&test->streams, streams);
- close(sp->socket);
- iperf_free_stream(sp);
+ if (sp->socket != -1) {
+ SLIST_REMOVE_HEAD(&test->streams, streams);
+ close(sp->socket);
+ sp->socket = -1;
+ iperf_free_stream(sp);
+ }
}
close(test->ctrl_sck);
test->ctrl_sck = -1;
@@ -382,6 +460,48 @@ static void
cleanup_server(struct iperf_test *test)
{
struct iperf_stream *sp;
+ int32_t err;
+
+ /* Try to send the error code to the client*/
+ if (i_errno != IENONE && test->ctrl_sck != -1) {
+ if (iperf_set_send_state(test, SERVER_ERROR) == 0) {
+ err = htonl(i_errno);
+ if (Nwrite(test->ctrl_sck, (char*) &err, sizeof(err), Ptcp) >= 0) {
+ err = htonl(errno);
+ Nwrite(test->ctrl_sck, (char*) &err, sizeof(err), Ptcp);
+ }
+ }
+ }
+
+ /* Cancel outstanding threads */
+ int i_errno_save = i_errno;
+ SLIST_FOREACH(sp, &test->streams, streams) {
+ int rc;
+ sp->done = 1;
+ if (sp->thread_created == 1) {
+ rc = pthread_cancel(sp->thr);
+ if (rc != 0 && rc != ESRCH) {
+ i_errno = IEPTHREADCANCEL;
+ errno = rc;
+ iperf_err(test, "cleanup_server in pthread_cancel - %s", iperf_strerror(i_errno));
+ }
+ rc = pthread_join(sp->thr, NULL);
+ if (rc != 0 && rc != ESRCH) {
+ i_errno = IEPTHREADJOIN;
+ errno = rc;
+ iperf_err(test, "cleanup_server in pthread_join - %s", iperf_strerror(i_errno));
+ }
+ if (test->debug_level >= DEBUG_LEVEL_INFO) {
+ iperf_printf(test, "Thread FD %d stopped\n", sp->socket);
+ }
+ sp->thread_created = 0;
+ }
+ }
+ i_errno = i_errno_save;
+
+ if (test->debug_level >= DEBUG_LEVEL_INFO) {
+ iperf_printf(test, "All threads stopped\n");
+ }
/* Close open streams */
SLIST_FOREACH(sp, &test->streams, streams) {
@@ -395,7 +515,8 @@ cleanup_server(struct iperf_test *test)
/* Close open test sockets */
if (test->ctrl_sck > -1) {
- close(test->ctrl_sck);
+ // Make sure all control messages (especially error messages) are received by the client before the socket is closed
+ iperf_sync_close_socket(test->ctrl_sck);
test->ctrl_sck = -1;
}
if (test->listener > -1) {
@@ -447,26 +568,31 @@ iperf_run_server(struct iperf_test *test)
struct iperf_time diff_time;
struct timeval* timeout;
struct timeval used_timeout;
+ iperf_size_t last_receive_blocks;
int flag;
int64_t t_usecs;
int64_t timeout_us;
int64_t rcv_timeout_us;
+ int32_t err;
- if (test->logfile)
+ if (test->logfile) {
if (iperf_open_logfile(test) < 0)
return -2;
+ }
- if (test->affinity != -1)
+ if (test->affinity != -1) {
if (iperf_setaffinity(test, test->affinity) != 0) {
cleanup_server(test);
return -2;
}
+ }
- if (test->json_output)
+ if (test->json_output) {
if (iperf_json_start(test) < 0) {
cleanup_server(test);
return -2;
}
+ }
if (test->json_output) {
cJSON_AddItemToObject(test->json_start, "version", cJSON_CreateString(version));
@@ -485,19 +611,39 @@ iperf_run_server(struct iperf_test *test)
}
iperf_time_now(&last_receive_time); // Initialize last time something was received
+ last_receive_blocks = 0;
- test->state = IPERF_START;
+ iperf_set_test_state(test, IPERF_START);
send_streams_accepted = 0;
rec_streams_accepted = 0;
rcv_timeout_us = (test->settings->rcv_timeout.secs * SEC_TO_US) + test->settings->rcv_timeout.usecs;
while (test->state != IPERF_DONE) {
- // Check if average transfer rate was exceeded (condition set in the callback routines)
+ // Check if average transfer rate was exceeded (condition set in the callback routines)
if (test->bitrate_limit_exceeded) {
- cleanup_server(test);
- i_errno = IETOTALRATE;
+ i_errno = IETOTALRATE;
+ if (iperf_set_send_state(test, SERVER_ERROR) != 0) {
+ cleanup_server(test);
+ return -1;
+ }
+
+ err = htonl(i_errno);
+ if (Nwrite(test->ctrl_sck, (char*) &err, sizeof(err), Ptcp) < 0) {
+ cleanup_server(test);
+ i_errno = IECTRLWRITE;
return -1;
+ }
+
+ err = htonl(errno);
+ if (Nwrite(test->ctrl_sck, (char*) &err, sizeof(err), Ptcp) < 0) {
+ cleanup_server(test);
+ i_errno = IECTRLWRITE;
+ return -1;
+ }
+
+ cleanup_server(test);
+ return -1;
}
memcpy(&read_set, &test->read_set, sizeof(fd_set));
@@ -520,6 +666,10 @@ iperf_run_server(struct iperf_test *test)
used_timeout.tv_usec = timeout->tv_usec;
timeout_us = (timeout->tv_sec * SEC_TO_US) + timeout->tv_usec;
}
+ /* Cap the maximum select timeout at 1 second */
+ if (timeout_us > SEC_TO_US) {
+ timeout_us = SEC_TO_US;
+ }
if (timeout_us < 0 || timeout_us > rcv_timeout_us) {
used_timeout.tv_sec = test->settings->rcv_timeout.secs;
used_timeout.tv_usec = test->settings->rcv_timeout.usecs;
@@ -533,13 +683,18 @@ iperf_run_server(struct iperf_test *test)
i_errno = IESELECT;
return -1;
} else if (result == 0) {
- // If nothing was received during the specified time (per state)
- // then probably something got stack either at the client, server or network,
- // and Test should be forced to end.
+ /*
+ * If nothing was received during the specified time (per
+ * state) then probably something got stuck either at the
+ * client, server or network, and test should be forced to
+ * end.
+ */
iperf_time_now(&now);
t_usecs = 0;
if (iperf_time_diff(&now, &last_receive_time, &diff_time) == 0) {
t_usecs = iperf_time_in_usecs(&diff_time);
+
+ /* We're in the state where we're still accepting connections */
if (test->state == IPERF_START) {
if (test->settings->idle_timeout > 0 && t_usecs >= test->settings->idle_timeout * SEC_TO_US) {
test->server_forced_idle_restarts_count += 1;
@@ -557,21 +712,33 @@ iperf_run_server(struct iperf_test *test)
return 2;
}
}
+
+ /*
+ * Running a test. If we're receiving, be sure we're making
+ * progress (sender hasn't died/crashed).
+ */
else if (test->mode != SENDER && t_usecs > rcv_timeout_us) {
- test->server_forced_no_msg_restarts_count += 1;
- i_errno = IENOMSG;
- if (iperf_get_verbose(test))
- iperf_err(test, "Server restart (#%d) during active test due to idle timeout for receiving data",
- test->server_forced_no_msg_restarts_count);
- cleanup_server(test);
- return -1;
+ /* Idle timeout if no new blocks received */
+ if (test->blocks_received == last_receive_blocks) {
+ test->server_forced_no_msg_restarts_count += 1;
+ i_errno = IENOMSG;
+ if (iperf_get_verbose(test))
+ iperf_err(test, "Server restart (#%d) during active test due to idle timeout for receiving data",
+ test->server_forced_no_msg_restarts_count);
+ cleanup_server(test);
+ return -1;
+ }
}
-
}
}
+ /* See if the test is making progress */
+ if (test->blocks_received > last_receive_blocks) {
+ last_receive_blocks = test->blocks_received;
+ last_receive_time = now;
+ }
+
if (result > 0) {
- iperf_time_now(&last_receive_time);
if (FD_ISSET(test->listener, &read_set)) {
if (test->state != CREATE_STREAMS) {
if (iperf_accept(test) < 0) {
@@ -709,24 +876,8 @@ iperf_run_server(struct iperf_test *test)
return -1;
}
- if (sp->sender)
- FD_SET(s, &test->write_set);
- else
- FD_SET(s, &test->read_set);
-
if (s > test->max_fd) test->max_fd = s;
- /*
- * If the protocol isn't UDP, or even if it is but
- * we're the receiver, set nonblocking sockets.
- * We need this to allow a server receiver to
- * maintain interactivity with the control channel.
- */
- if (test->protocol->id != Pudp ||
- !sp->sender) {
- setnonblocking(s, 1);
- }
-
if (test->on_new_stream)
test->on_new_stream(sp);
@@ -798,33 +949,38 @@ iperf_run_server(struct iperf_test *test)
cleanup_server(test);
return -1;
}
- }
- }
- if (test->state == TEST_RUNNING) {
- if (test->mode == BIDIRECTIONAL) {
- if (iperf_recv(test, &read_set) < 0) {
+ /* Create and spin up threads */
+ pthread_attr_t attr;
+ if (pthread_attr_init(&attr) != 0) {
+ i_errno = IEPTHREADATTRINIT;
cleanup_server(test);
- return -1;
+ };
+
+ SLIST_FOREACH(sp, &test->streams, streams) {
+ if (pthread_create(&(sp->thr), &attr, &iperf_server_worker_run, sp) != 0) {
+ i_errno = IEPTHREADCREATE;
+ cleanup_server(test);
+ return -1;
+ }
+ sp->thread_created = 1;
+ if (test->debug_level >= DEBUG_LEVEL_INFO) {
+ iperf_printf(test, "Thread FD %d created\n", sp->socket);
+ }
}
- if (iperf_send(test, &write_set) < 0) {
- cleanup_server(test);
- return -1;
+ if (test->debug_level >= DEBUG_LEVEL_INFO) {
+ iperf_printf(test, "All threads created\n");
}
- } else if (test->mode == SENDER) {
- // Reverse mode. Server sends.
- if (iperf_send(test, &write_set) < 0) {
- cleanup_server(test);
- return -1;
- }
- } else {
- // Regular mode. Server receives.
- if (iperf_recv(test, &read_set) < 0) {
- cleanup_server(test);
- return -1;
- }
+ if (pthread_attr_destroy(&attr) != 0) {
+ i_errno = IEPTHREADATTRDESTROY;
+ cleanup_server(test);
+ };
+
+ /* Reset receive-progress baseline after workers are ready */
+ last_receive_blocks = test->blocks_received;
+ iperf_time_now(&last_receive_time);
}
- }
+ }
}
if (result == 0 ||
diff --git a/src/iperf_tcp.c b/src/iperf_tcp.c
index ce6a5221b..700e65c0d 100644
--- a/src/iperf_tcp.c
+++ b/src/iperf_tcp.c
@@ -41,6 +41,7 @@
#include "iperf.h"
#include "iperf_api.h"
#include "iperf_tcp.h"
+#include "iperf_util.h"
#include "net.h"
#include "cjson.h"
@@ -56,20 +57,28 @@ int
iperf_tcp_recv(struct iperf_stream *sp)
{
int r;
+ int sock_opt;
+
+#if defined(HAVE_MSG_TRUNC)
+ sock_opt = sp->test->settings->skip_rx_copy ? MSG_TRUNC : 0;
+#else
+ sock_opt = 0;
+#endif /* HAVE_MSG_TRUNC */
+
+ r = Nrecv_no_select(sp->socket, sp->buffer, sp->settings->blksize, Ptcp, sock_opt);
- r = Nread(sp->socket, sp->buffer, sp->settings->blksize, Ptcp);
if (r < 0)
return r;
/* Only count bytes received while we're in the correct state. */
if (sp->test->state == TEST_RUNNING) {
- sp->result->bytes_received += r;
- sp->result->bytes_received_this_interval += r;
+ sp->result->bytes_received += r;
+ sp->result->bytes_received_this_interval += r;
}
else {
- if (sp->test->debug)
- printf("Late receive, state = %d\n", sp->test->state);
+ if (sp->test->debug)
+ printf("Late receive, state = %d-%s\n", sp->test->state, state_to_text(sp->test->state));
}
return r;
@@ -86,12 +95,12 @@ iperf_tcp_send(struct iperf_stream *sp)
int r;
if (!sp->pending_size)
- sp->pending_size = sp->settings->blksize;
+ sp->pending_size = sp->settings->blksize;
if (sp->test->zerocopy)
- r = Nsendfile(sp->buffer_fd, sp->socket, sp->buffer, sp->pending_size);
+ r = Nsendfile(sp->buffer_fd, sp->socket, sp->buffer, sp->pending_size);
else
- r = Nwrite(sp->socket, sp->buffer, sp->pending_size, Ptcp);
+ r = Nwrite(sp->socket, sp->buffer, sp->pending_size, Ptcp);
if (r < 0)
return r;
@@ -101,8 +110,8 @@ iperf_tcp_send(struct iperf_stream *sp)
sp->result->bytes_sent_this_interval += r;
if (sp->test->debug_level >= DEBUG_LEVEL_DEBUG)
- printf("sent %d bytes of %d, pending %d, total %" PRIu64 "\n",
- r, sp->settings->blksize, sp->pending_size, sp->result->bytes_sent);
+ printf("sent %d bytes of %d, pending %d, total %" PRIu64 "\n",
+ r, sp->settings->blksize, sp->pending_size, sp->result->bytes_sent);
return r;
}
@@ -117,7 +126,7 @@ iperf_tcp_accept(struct iperf_test * test)
{
int s;
signed char rbuf = ACCESS_DENIED;
- char cookie[COOKIE_SIZE];
+ char cookie[COOKIE_SIZE] = {0};
socklen_t len;
struct sockaddr_storage addr;
@@ -126,13 +135,30 @@ iperf_tcp_accept(struct iperf_test * test)
i_errno = IESTREAMCONNECT;
return -1;
}
+#if defined(HAVE_SO_MAX_PACING_RATE)
+ /* If fq socket pacing is specified, enable it. */
+
+ if (test->settings->fqrate) {
+ /* Convert bits per second to bytes per second */
+ uint64_t fqrate = test->settings->fqrate / 8;
+ if (fqrate > 0) {
+ if (test->debug) {
+ printf("Setting fair-queue socket pacing to %"PRIu64"\n", fqrate);
+ }
+ if (setsockopt(s, SOL_SOCKET, SO_MAX_PACING_RATE, &fqrate, sizeof(fqrate)) < 0) {
+ warning("Unable to set socket pacing");
+ }
+ }
+ }
+#endif /* HAVE_SO_MAX_PACING_RATE */
if (Nread(s, cookie, COOKIE_SIZE, Ptcp) < 0) {
i_errno = IERECVCOOKIE;
+ close(s);
return -1;
}
- if (strcmp(test->cookie, cookie) != 0) {
+ if (strncmp(test->cookie, cookie, COOKIE_SIZE) != 0) {
if (Nwrite(s, (char*) &rbuf, sizeof(rbuf), Ptcp) < 0) {
iperf_err(test, "failed to send access denied from busy server to new connecting client, errno = %d\n", errno);
}
@@ -166,9 +192,10 @@ iperf_tcp_listen(struct iperf_test *test)
*
* It's not clear whether this is a requirement or a convenience.
*/
- if (test->no_delay || test->settings->mss || test->settings->socket_bufsize) {
+ if (test->no_delay || test->mptcp || test->settings->mss || test->settings->socket_bufsize) {
struct addrinfo hints, *res;
char portstr[6];
+ int proto = 0;
FD_CLR(s, &test->read_set);
close(s);
@@ -179,7 +206,7 @@ iperf_tcp_listen(struct iperf_test *test)
/*
* If binding to the wildcard address with no explicit address
* family specified, then force us to get an AF_INET6 socket.
- * More details in the comments in netanounce().
+ * More details in the comments in netannounce().
*/
if (test->settings->domain == AF_UNSPEC && !test->bind_address) {
hints.ai_family = AF_INET6;
@@ -194,7 +221,12 @@ iperf_tcp_listen(struct iperf_test *test)
return -1;
}
- if ((s = socket(res->ai_family, SOCK_STREAM, 0)) < 0) {
+#if defined(HAVE_IPPROTO_MPTCP)
+ if (test->mptcp)
+ proto = IPPROTO_MPTCP;
+#endif
+
+ if ((s = socket(res->ai_family, SOCK_STREAM, proto)) < 0) {
freeaddrinfo(res);
i_errno = IESTREAMLISTEN;
return -1;
@@ -240,21 +272,6 @@ iperf_tcp_listen(struct iperf_test *test)
return -1;
}
}
-#if defined(HAVE_SO_MAX_PACING_RATE)
- /* If fq socket pacing is specified, enable it. */
- if (test->settings->fqrate) {
- /* Convert bits per second to bytes per second */
- unsigned int fqrate = test->settings->fqrate / 8;
- if (fqrate > 0) {
- if (test->debug) {
- printf("Setting fair-queue socket pacing to %u\n", fqrate);
- }
- if (setsockopt(s, SOL_SOCKET, SO_MAX_PACING_RATE, &fqrate, sizeof(fqrate)) < 0) {
- warning("Unable to set socket pacing");
- }
- }
- }
-#endif /* HAVE_SO_MAX_PACING_RATE */
{
unsigned int rate = test->settings->rate / 8;
if (rate > 0) {
@@ -309,6 +326,7 @@ iperf_tcp_listen(struct iperf_test *test)
if (listen(s, INT_MAX) < 0) {
i_errno = IESTREAMLISTEN;
+ close(s);
return -1;
}
@@ -329,6 +347,7 @@ iperf_tcp_listen(struct iperf_test *test)
}
if (test->settings->socket_bufsize && test->settings->socket_bufsize > sndbuf_actual) {
i_errno = IESETBUF2;
+ close(s);
return -1;
}
@@ -346,6 +365,7 @@ iperf_tcp_listen(struct iperf_test *test)
}
if (test->settings->socket_bufsize && test->settings->socket_bufsize > rcvbuf_actual) {
i_errno = IESETBUF2;
+ close(s);
return -1;
}
@@ -374,8 +394,14 @@ iperf_tcp_connect(struct iperf_test *test)
socklen_t optlen;
int saved_errno;
int rcvbuf_actual, sndbuf_actual;
+ int proto = 0;
+
+#if defined(HAVE_IPPROTO_MPTCP)
+ if (test->mptcp)
+ proto = IPPROTO_MPTCP;
+#endif
- s = create_socket(test->settings->domain, SOCK_STREAM, test->bind_address, test->bind_dev, test->bind_port, test->server_hostname, test->server_port, &server_res);
+ s = create_socket(test->settings->domain, SOCK_STREAM, proto, test->bind_address, test->bind_dev, test->bind_port, test->server_hostname, test->server_port, &server_res);
if (s < 0) {
i_errno = IESTREAMCONNECT;
return -1;
@@ -448,6 +474,8 @@ iperf_tcp_connect(struct iperf_test *test)
printf("SNDBUF is %u, expecting %u\n", sndbuf_actual, test->settings->socket_bufsize);
}
if (test->settings->socket_bufsize && test->settings->socket_bufsize > sndbuf_actual) {
+ close(s);
+ freeaddrinfo(server_res);
i_errno = IESETBUF2;
return -1;
}
@@ -466,6 +494,8 @@ iperf_tcp_connect(struct iperf_test *test)
printf("RCVBUF is %u, expecting %u\n", rcvbuf_actual, test->settings->socket_bufsize);
}
if (test->settings->socket_bufsize && test->settings->socket_bufsize > rcvbuf_actual) {
+ close(s);
+ freeaddrinfo(server_res);
i_errno = IESETBUF2;
return -1;
}
@@ -536,10 +566,10 @@ iperf_tcp_connect(struct iperf_test *test)
/* If socket pacing is specified try to enable it. */
if (test->settings->fqrate) {
/* Convert bits per second to bytes per second */
- unsigned int fqrate = test->settings->fqrate / 8;
+ uint64_t fqrate = test->settings->fqrate / 8;
if (fqrate > 0) {
if (test->debug) {
- printf("Setting fair-queue socket pacing to %u\n", fqrate);
+ printf("Setting fair-queue socket pacing to %"PRIu64"\n", fqrate);
}
if (setsockopt(s, SOL_SOCKET, SO_MAX_PACING_RATE, &fqrate, sizeof(fqrate)) < 0) {
warning("Unable to set socket pacing");
diff --git a/src/iperf_tcp.h b/src/iperf_tcp.h
index d53b0524d..9dbb2ee10 100644
--- a/src/iperf_tcp.h
+++ b/src/iperf_tcp.h
@@ -48,7 +48,7 @@ int iperf_tcp_recv(struct iperf_stream *);
/**
* iperf_tcp_send -- sends the client data for TCP
- * and the Param/result message exchanges
+ * and the Param/result message exchanges
* returns: bytes sent
*
*/
diff --git a/src/iperf_time.c b/src/iperf_time.c
index a435dd30d..86ba78edf 100644
--- a/src/iperf_time.c
+++ b/src/iperf_time.c
@@ -36,33 +36,52 @@
#include
int
-iperf_time_now(struct iperf_time *time1)
+clock_gettime_helper(struct iperf_time *now, clockid_t clk_id)
{
struct timespec ts;
int result;
- result = clock_gettime(CLOCK_MONOTONIC, &ts);
+ result = clock_gettime(clk_id, &ts);
if (result == 0) {
- time1->secs = (uint32_t) ts.tv_sec;
- time1->usecs = (uint32_t) ts.tv_nsec / 1000;
+ now->secs = (uint32_t) ts.tv_sec;
+ now->usecs = (uint32_t) ts.tv_nsec / 1000;
}
return result;
}
+int
+iperf_time_now(struct iperf_time *now)
+{
+ return clock_gettime_helper(now, CLOCK_MONOTONIC);
+}
+
+int
+iperf_time_now_wallclock(struct iperf_time *now)
+{
+ return clock_gettime_helper(now, CLOCK_REALTIME);
+}
+
#else
#include
int
-iperf_time_now(struct iperf_time *time1)
+iperf_time_now_wallclock(struct iperf_time *now)
{
struct timeval tv;
int result;
+ // Returns the non monotonic local wallclock time.
result = gettimeofday(&tv, NULL);
- time1->secs = tv.tv_sec;
- time1->usecs = tv.tv_usec;
+ now->secs = (uint32_t) tv.tv_sec;
+ now->usecs = (uint32_t) tv.tv_usec;
return result;
}
+int
+iperf_time_now(struct iperf_time *now)
+{
+ return iperf_time_now_wallclock(now);
+}
+
#endif
/* iperf_time_add_usecs
@@ -72,12 +91,11 @@ iperf_time_now(struct iperf_time *time1)
void
iperf_time_add_usecs(struct iperf_time *time1, uint64_t usecs)
{
- time1->secs += usecs / 1000000L;
- time1->usecs += usecs % 1000000L;
- if ( time1->usecs >= 1000000L ) {
- time1->secs += time1->usecs / 1000000L;
- time1->usecs %= 1000000L;
- }
+ uint64_t total_usecs;
+
+ total_usecs = time1->usecs + usecs;
+ time1->secs += total_usecs / 1000000L;
+ time1->usecs = total_usecs % 1000000L;
}
uint64_t
diff --git a/src/iperf_time.h b/src/iperf_time.h
index 588ee2624..3f46b496f 100644
--- a/src/iperf_time.h
+++ b/src/iperf_time.h
@@ -34,6 +34,24 @@ struct iperf_time {
uint32_t usecs;
};
+/**
+ * Retrieves the current wallclock time.
+ *
+ * When during operation the system-wide clock changes, users will see
+ * this and results might become inconsistent.
+ */
+int iperf_time_now_wallclock(struct iperf_time *time1);
+
+/**
+ * Retrieves the current time using a monotonic clock.
+ *
+ * When during operation the system-wide clock changes, users still will see
+ * a strictly monotonic increasing clock.
+ *
+ * Please note that a monotonic clock is only available on systems that have the
+ * `clock_gettime()` system call. On other systems, this falls back to
+ * `iperf_time_now_wallclock()`.
+ */
int iperf_time_now(struct iperf_time *time1);
void iperf_time_add_usecs(struct iperf_time *time1, uint64_t usecs);
@@ -44,6 +62,9 @@ int iperf_time_diff(struct iperf_time *time1, struct iperf_time *time2, struct i
uint64_t iperf_time_in_usecs(struct iperf_time *time);
+/**
+ * Returns the time in seconds as double type with a microsecond granularity.
+ */
double iperf_time_in_secs(struct iperf_time *time);
#endif
diff --git a/src/iperf_udp.c b/src/iperf_udp.c
index 5dc1422dc..393836c43 100644
--- a/src/iperf_udp.c
+++ b/src/iperf_udp.c
@@ -1,5 +1,5 @@
/*
- * iperf, Copyright (c) 2014-2022, The Regents of the University of
+ * iperf, Copyright (c) 2014-2026, The Regents of the University of
* California, through Lawrence Berkeley National Laboratory (subject
* to receipt of any required approvals from the U.S. Dept. of
* Energy). All rights reserved.
@@ -24,6 +24,8 @@
* This code is distributed under a BSD style license, see the LICENSE
* file for complete information.
*/
+#include "iperf_config.h"
+
#include
#include