summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/Makefile.am102
-rw-r--r--src/Makefile.in1919
-rw-r--r--src/cjson.c3150
-rw-r--r--src/cjson.h304
-rw-r--r--src/dscp.c162
-rw-r--r--src/flowlabel.h78
-rw-r--r--src/iperf.h463
-rw-r--r--src/iperf3.1507
-rw-r--r--src/iperf_api.c5037
-rw-r--r--src/iperf_api.h497
-rw-r--r--src/iperf_auth.c438
-rw-r--r--src/iperf_auth.h41
-rw-r--r--src/iperf_client_api.c819
-rw-r--r--src/iperf_config.h.in160
-rw-r--r--src/iperf_error.c527
-rw-r--r--src/iperf_locale.c568
-rw-r--r--src/iperf_locale.h134
-rw-r--r--src/iperf_sctp.c736
-rw-r--r--src/iperf_sctp.h68
-rw-r--r--src/iperf_server_api.c918
-rw-r--r--src/iperf_tcp.c583
-rw-r--r--src/iperf_tcp.h63
-rw-r--r--src/iperf_time.c156
-rw-r--r--src/iperf_time.h49
-rw-r--r--src/iperf_udp.c635
-rw-r--r--src/iperf_udp.h64
-rw-r--r--src/iperf_util.c597
-rw-r--r--src/iperf_util.h67
-rw-r--r--src/libiperf.3114
-rw-r--r--src/main.c214
-rw-r--r--src/net.c607
-rw-r--r--src/net.h45
-rw-r--r--src/portable_endian.h164
-rw-r--r--src/private.pem27
-rw-r--r--src/public.pem9
-rw-r--r--src/queue.h527
-rw-r--r--src/t_api.c92
-rw-r--r--src/t_auth.c125
-rw-r--r--src/t_timer.c83
-rw-r--r--src/t_units.c110
-rw-r--r--src/t_uuid.c46
-rw-r--r--src/tcp_info.c234
-rw-r--r--src/timer.c245
-rw-r--r--src/timer.h100
-rw-r--r--src/units.c355
-rw-r--r--src/units.h34
-rw-r--r--src/version.h.in27
47 files changed, 22000 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..11d3e17
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,102 @@
+lib_LTLIBRARIES = libiperf.la # Build and install an iperf library
+bin_PROGRAMS = iperf3 # Build and install an iperf binary
+if ENABLE_PROFILING
+noinst_PROGRAMS = t_timer t_units t_uuid t_api t_auth iperf3_profile # Build, but don't install the test programs and a profiled version of iperf3
+else
+noinst_PROGRAMS = t_timer t_units t_uuid t_api t_auth # Build, but don't install the test programs
+endif
+include_HEADERS = iperf_api.h # Defines the headers that get installed with the program
+
+
+# Specify the source files and flags for the iperf library
+libiperf_la_SOURCES = \
+ cjson.c \
+ cjson.h \
+ flowlabel.h \
+ iperf.h \
+ iperf_api.c \
+ iperf_api.h \
+ iperf_error.c \
+ iperf_auth.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
+
+# Specify the sources and various flags for the iperf binary
+iperf3_SOURCES = main.c
+iperf3_CFLAGS = -g
+iperf3_LDADD = libiperf.la
+iperf3_LDFLAGS = -g
+
+if ENABLE_PROFILING
+# If the iperf-profiled-binary is enabled
+# Specify the sources and various flags for the profiled iperf binary. This
+# binary recompiles all the source files to make sure they are all profiled.
+iperf3_profile_SOURCES = main.c \
+ $(libiperf_la_SOURCES)
+
+iperf3_profile_CFLAGS = -pg -g
+iperf3_profile_LDADD = libiperf.la
+iperf3_profile_LDFLAGS = -pg -g
+endif
+
+# Specify the sources and various flags for the test cases
+t_timer_SOURCES = t_timer.c
+t_timer_CFLAGS = -g
+t_timer_LDFLAGS =
+t_timer_LDADD = libiperf.la
+
+t_units_SOURCES = t_units.c
+t_units_CFLAGS = -g
+t_units_LDFLAGS =
+t_units_LDADD = libiperf.la
+
+t_uuid_SOURCES = t_uuid.c
+t_uuid_CFLAGS = -g
+t_uuid_LDFLAGS =
+t_uuid_LDADD = libiperf.la
+
+t_api_SOURCES = t_api.c
+t_api_CFLAGS = -g
+t_api_LDFLAGS =
+t_api_LDADD = libiperf.la
+
+t_auth_SOURCES = t_auth.c
+t_auth_CFLAGS = -g
+t_auth_LDFLAGS =
+t_auth_LDADD = libiperf.la
+
+
+
+# Specify which tests to run during a "make check"
+TESTS = \
+ t_timer \
+ t_units \
+ t_uuid \
+ t_api \
+ t_auth
+
+dist_man_MANS = iperf3.1 libiperf.3
diff --git a/src/Makefile.in b/src/Makefile.in
new file mode 100644
index 0000000..e13e4ed
--- /dev/null
+++ b/src/Makefile.in
@@ -0,0 +1,1919 @@
+# Makefile.in generated by automake 1.16.5 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2021 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ 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))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+bin_PROGRAMS = iperf3$(EXEEXT)
+@ENABLE_PROFILING_FALSE@noinst_PROGRAMS = t_timer$(EXEEXT) \
+@ENABLE_PROFILING_FALSE@ t_units$(EXEEXT) t_uuid$(EXEEXT) \
+@ENABLE_PROFILING_FALSE@ t_api$(EXEEXT) t_auth$(EXEEXT)
+@ENABLE_PROFILING_TRUE@noinst_PROGRAMS = t_timer$(EXEEXT) \
+@ENABLE_PROFILING_TRUE@ t_units$(EXEEXT) t_uuid$(EXEEXT) \
+@ENABLE_PROFILING_TRUE@ t_api$(EXEEXT) t_auth$(EXEEXT) \
+@ENABLE_PROFILING_TRUE@ iperf3_profile$(EXEEXT)
+TESTS = t_timer$(EXEEXT) t_units$(EXEEXT) t_uuid$(EXEEXT) \
+ t_api$(EXEEXT) t_auth$(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) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(include_HEADERS) \
+ $(am__DIST_COMMON)
+mkinstalldirs = $(SHELL) $(top_srcdir)/config/mkinstalldirs
+CONFIG_HEADER = iperf_config.h
+CONFIG_CLEAN_FILES = version.h
+CONFIG_CLEAN_VPATH_FILES =
+am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(libdir)" \
+ "$(DESTDIR)$(man1dir)" "$(DESTDIR)$(man3dir)" \
+ "$(DESTDIR)$(includedir)"
+PROGRAMS = $(bin_PROGRAMS) $(noinst_PROGRAMS)
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+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; }; \
+ }
+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
+libiperf_la_OBJECTS = $(am_libiperf_la_OBJECTS)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+am_iperf3_OBJECTS = iperf3-main.$(OBJEXT)
+iperf3_OBJECTS = $(am_iperf3_OBJECTS)
+iperf3_DEPENDENCIES = libiperf.la
+iperf3_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(iperf3_CFLAGS) $(CFLAGS) \
+ $(iperf3_LDFLAGS) $(LDFLAGS) -o $@
+am__iperf3_profile_SOURCES_DIST = main.c cjson.c cjson.h flowlabel.h \
+ iperf.h iperf_api.c iperf_api.h iperf_error.c iperf_auth.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
+am__objects_1 = iperf3_profile-cjson.$(OBJEXT) \
+ iperf3_profile-iperf_api.$(OBJEXT) \
+ iperf3_profile-iperf_error.$(OBJEXT) \
+ iperf3_profile-iperf_auth.$(OBJEXT) \
+ iperf3_profile-iperf_client_api.$(OBJEXT) \
+ iperf3_profile-iperf_locale.$(OBJEXT) \
+ iperf3_profile-iperf_server_api.$(OBJEXT) \
+ iperf3_profile-iperf_tcp.$(OBJEXT) \
+ iperf3_profile-iperf_udp.$(OBJEXT) \
+ iperf3_profile-iperf_sctp.$(OBJEXT) \
+ iperf3_profile-iperf_util.$(OBJEXT) \
+ iperf3_profile-iperf_time.$(OBJEXT) \
+ iperf3_profile-dscp.$(OBJEXT) iperf3_profile-net.$(OBJEXT) \
+ iperf3_profile-tcp_info.$(OBJEXT) \
+ iperf3_profile-timer.$(OBJEXT) iperf3_profile-units.$(OBJEXT)
+@ENABLE_PROFILING_TRUE@am_iperf3_profile_OBJECTS = \
+@ENABLE_PROFILING_TRUE@ iperf3_profile-main.$(OBJEXT) \
+@ENABLE_PROFILING_TRUE@ $(am__objects_1)
+iperf3_profile_OBJECTS = $(am_iperf3_profile_OBJECTS)
+@ENABLE_PROFILING_TRUE@iperf3_profile_DEPENDENCIES = libiperf.la
+iperf3_profile_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(iperf3_profile_CFLAGS) $(CFLAGS) $(iperf3_profile_LDFLAGS) \
+ $(LDFLAGS) -o $@
+am_t_api_OBJECTS = t_api-t_api.$(OBJEXT)
+t_api_OBJECTS = $(am_t_api_OBJECTS)
+t_api_DEPENDENCIES = libiperf.la
+t_api_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(t_api_CFLAGS) $(CFLAGS) \
+ $(t_api_LDFLAGS) $(LDFLAGS) -o $@
+am_t_auth_OBJECTS = t_auth-t_auth.$(OBJEXT)
+t_auth_OBJECTS = $(am_t_auth_OBJECTS)
+t_auth_DEPENDENCIES = libiperf.la
+t_auth_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(t_auth_CFLAGS) $(CFLAGS) \
+ $(t_auth_LDFLAGS) $(LDFLAGS) -o $@
+am_t_timer_OBJECTS = t_timer-t_timer.$(OBJEXT)
+t_timer_OBJECTS = $(am_t_timer_OBJECTS)
+t_timer_DEPENDENCIES = libiperf.la
+t_timer_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(t_timer_CFLAGS) \
+ $(CFLAGS) $(t_timer_LDFLAGS) $(LDFLAGS) -o $@
+am_t_units_OBJECTS = t_units-t_units.$(OBJEXT)
+t_units_OBJECTS = $(am_t_units_OBJECTS)
+t_units_DEPENDENCIES = libiperf.la
+t_units_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(t_units_CFLAGS) \
+ $(CFLAGS) $(t_units_LDFLAGS) $(LDFLAGS) -o $@
+am_t_uuid_OBJECTS = t_uuid-t_uuid.$(OBJEXT)
+t_uuid_OBJECTS = $(am_t_uuid_OBJECTS)
+t_uuid_DEPENDENCIES = libiperf.la
+t_uuid_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(t_uuid_CFLAGS) $(CFLAGS) \
+ $(t_uuid_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@
+depcomp = $(SHELL) $(top_srcdir)/config/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/cjson.Plo ./$(DEPDIR)/dscp.Plo \
+ ./$(DEPDIR)/iperf3-main.Po ./$(DEPDIR)/iperf3_profile-cjson.Po \
+ ./$(DEPDIR)/iperf3_profile-dscp.Po \
+ ./$(DEPDIR)/iperf3_profile-iperf_api.Po \
+ ./$(DEPDIR)/iperf3_profile-iperf_auth.Po \
+ ./$(DEPDIR)/iperf3_profile-iperf_client_api.Po \
+ ./$(DEPDIR)/iperf3_profile-iperf_error.Po \
+ ./$(DEPDIR)/iperf3_profile-iperf_locale.Po \
+ ./$(DEPDIR)/iperf3_profile-iperf_sctp.Po \
+ ./$(DEPDIR)/iperf3_profile-iperf_server_api.Po \
+ ./$(DEPDIR)/iperf3_profile-iperf_tcp.Po \
+ ./$(DEPDIR)/iperf3_profile-iperf_time.Po \
+ ./$(DEPDIR)/iperf3_profile-iperf_udp.Po \
+ ./$(DEPDIR)/iperf3_profile-iperf_util.Po \
+ ./$(DEPDIR)/iperf3_profile-main.Po \
+ ./$(DEPDIR)/iperf3_profile-net.Po \
+ ./$(DEPDIR)/iperf3_profile-tcp_info.Po \
+ ./$(DEPDIR)/iperf3_profile-timer.Po \
+ ./$(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
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(libiperf_la_SOURCES) $(iperf3_SOURCES) \
+ $(iperf3_profile_SOURCES) $(t_api_SOURCES) $(t_auth_SOURCES) \
+ $(t_timer_SOURCES) $(t_units_SOURCES) $(t_uuid_SOURCES)
+DIST_SOURCES = $(libiperf_la_SOURCES) $(iperf3_SOURCES) \
+ $(am__iperf3_profile_SOURCES_DIST) $(t_api_SOURCES) \
+ $(t_auth_SOURCES) $(t_timer_SOURCES) $(t_units_SOURCES) \
+ $(t_uuid_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+man1dir = $(mandir)/man1
+man3dir = $(mandir)/man3
+NROFF = nroff
+MANS = $(dist_man_MANS)
+HEADERS = $(include_HEADERS)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) \
+ iperf_config.h.in
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+am__tty_colors_dummy = \
+ mgn= red= grn= lgn= blu= brg= std=; \
+ am__color_tests=no
+am__tty_colors = { \
+ $(am__tty_colors_dummy); \
+ if test "X$(AM_COLOR_TESTS)" = Xno; then \
+ am__color_tests=no; \
+ elif test "X$(AM_COLOR_TESTS)" = Xalways; then \
+ am__color_tests=yes; \
+ elif test "X$$TERM" != Xdumb && { test -t 1; } 2>/dev/null; then \
+ am__color_tests=yes; \
+ fi; \
+ if test $$am__color_tests = yes; then \
+ red=''; \
+ grn=''; \
+ lgn=''; \
+ blu=''; \
+ mgn=''; \
+ brg=''; \
+ std=''; \
+ fi; \
+}
+am__recheck_rx = ^[ ]*:recheck:[ ]*
+am__global_test_result_rx = ^[ ]*:global-test-result:[ ]*
+am__copy_in_global_log_rx = ^[ ]*:copy-in-global-log:[ ]*
+# A command that, given a newline-separated list of test names on the
+# standard input, print the name of the tests that are to be re-run
+# upon "make recheck".
+am__list_recheck_tests = $(AWK) '{ \
+ recheck = 1; \
+ while ((rc = (getline line < ($$0 ".trs"))) != 0) \
+ { \
+ if (rc < 0) \
+ { \
+ if ((getline line2 < ($$0 ".log")) < 0) \
+ recheck = 0; \
+ break; \
+ } \
+ else if (line ~ /$(am__recheck_rx)[nN][Oo]/) \
+ { \
+ recheck = 0; \
+ break; \
+ } \
+ else if (line ~ /$(am__recheck_rx)[yY][eE][sS]/) \
+ { \
+ break; \
+ } \
+ }; \
+ if (recheck) \
+ print $$0; \
+ close ($$0 ".trs"); \
+ close ($$0 ".log"); \
+}'
+# A command that, given a newline-separated list of test names on the
+# standard input, create the global log from their .trs and .log files.
+am__create_global_log = $(AWK) ' \
+function fatal(msg) \
+{ \
+ print "fatal: making $@: " msg | "cat >&2"; \
+ exit 1; \
+} \
+function rst_section(header) \
+{ \
+ print header; \
+ len = length(header); \
+ for (i = 1; i <= len; i = i + 1) \
+ printf "="; \
+ printf "\n\n"; \
+} \
+{ \
+ copy_in_global_log = 1; \
+ global_test_result = "RUN"; \
+ while ((rc = (getline line < ($$0 ".trs"))) != 0) \
+ { \
+ if (rc < 0) \
+ fatal("failed to read from " $$0 ".trs"); \
+ if (line ~ /$(am__global_test_result_rx)/) \
+ { \
+ sub("$(am__global_test_result_rx)", "", line); \
+ sub("[ ]*$$", "", line); \
+ global_test_result = line; \
+ } \
+ else if (line ~ /$(am__copy_in_global_log_rx)[nN][oO]/) \
+ copy_in_global_log = 0; \
+ }; \
+ if (copy_in_global_log) \
+ { \
+ rst_section(global_test_result ": " $$0); \
+ while ((rc = (getline line < ($$0 ".log"))) != 0) \
+ { \
+ if (rc < 0) \
+ fatal("failed to read from " $$0 ".log"); \
+ print line; \
+ }; \
+ printf "\n"; \
+ }; \
+ close ($$0 ".trs"); \
+ close ($$0 ".log"); \
+}'
+# Restructured Text title.
+am__rst_title = { sed 's/.*/ & /;h;s/./=/g;p;x;s/ *$$//;p;g' && echo; }
+# Solaris 10 'make', and several other traditional 'make' implementations,
+# pass "-e" to $(SHELL), and POSIX 2008 even requires this. Work around it
+# by disabling -e (using the XSI extension "set +e") if it's set.
+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" \
+ --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
+# passes TESTS_ENVIRONMENT. Set up options for the wrapper that
+# will run the test scripts (or their associated LOG_COMPILER, if
+# thy have one).
+am__check_pre = \
+$(am__sh_e_setup); \
+$(am__vpath_adj_setup) $(am__vpath_adj) \
+$(am__tty_colors); \
+srcdir=$(srcdir); export srcdir; \
+case "$@" in \
+ */*) am__odir=`echo "./$@" | sed 's|/[^/]*$$||'`;; \
+ *) am__odir=.;; \
+esac; \
+test "x$$am__odir" = x"." || test -d "$$am__odir" \
+ || $(MKDIR_P) "$$am__odir" || exit $$?; \
+if test -f "./$$f"; then dir=./; \
+elif test -f "$$f"; then dir=; \
+else dir="$(srcdir)/"; fi; \
+tst=$$dir$$f; log='$@'; \
+if test -n '$(DISABLE_HARD_ERRORS)'; then \
+ am__enable_hard_errors=no; \
+else \
+ am__enable_hard_errors=yes; \
+fi; \
+case " $(XFAIL_TESTS) " in \
+ *[\ \ ]$$f[\ \ ]* | *[\ \ ]$$dir$$f[\ \ ]*) \
+ am__expect_failure=yes;; \
+ *) \
+ am__expect_failure=no;; \
+esac; \
+$(AM_TESTS_ENVIRONMENT) $(TESTS_ENVIRONMENT)
+# A shell command to get the names of the tests scripts with any registered
+# extension removed (i.e., equivalently, the names of the test logs, with
+# the '.log' extension removed). The result is saved in the shell variable
+# '$bases'. This honors runtime overriding of TESTS and TEST_LOGS. Sadly,
+# we cannot use something simpler, involving e.g., "$(TEST_LOGS:.log=)",
+# since that might cause problem with VPATH rewrites for suffix-less tests.
+# See also 'test-harness-vpath-rewrite.sh' and 'test-trs-basic.sh'.
+am__set_TESTS_bases = \
+ bases='$(TEST_LOGS)'; \
+ bases=`for i in $$bases; do echo $$i; done | sed 's/\.log$$//'`; \
+ bases=`echo $$bases`
+AM_TESTSUITE_SUMMARY_HEADER = ' for $(PACKAGE_STRING)'
+RECHECK_LOGS = $(TEST_LOGS)
+AM_RECURSIVE_TARGETS = check recheck
+TEST_SUITE_LOG = test-suite.log
+TEST_EXTENSIONS = @EXEEXT@ .test
+LOG_DRIVER = $(SHELL) $(top_srcdir)/config/test-driver
+LOG_COMPILE = $(LOG_COMPILER) $(AM_LOG_FLAGS) $(LOG_FLAGS)
+am__set_b = \
+ case '$@' in \
+ */*) \
+ case '$*' in \
+ */*) b='$*';; \
+ *) b=`echo '$@' | sed 's/\.log$$//'`; \
+ esac;; \
+ *) \
+ b='$*';; \
+ esac
+am__test_logs1 = $(TESTS:=.log)
+am__test_logs2 = $(am__test_logs1:@EXEEXT@.log=.log)
+TEST_LOGS = $(am__test_logs2:.test.log=.log)
+TEST_LOG_DRIVER = $(SHELL) $(top_srcdir)/config/test-driver
+TEST_LOG_COMPILE = $(TEST_LOG_COMPILER) $(AM_TEST_LOG_FLAGS) \
+ $(TEST_LOG_FLAGS)
+am__DIST_COMMON = $(dist_man_MANS) $(srcdir)/Makefile.in \
+ $(srcdir)/iperf_config.h.in $(srcdir)/version.h.in \
+ $(top_srcdir)/config/depcomp \
+ $(top_srcdir)/config/mkinstalldirs \
+ $(top_srcdir)/config/test-driver
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CSCOPE = @CSCOPE@
+CTAGS = @CTAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DLLTOOL = @DLLTOOL@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+ETAGS = @ETAGS@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FILECMD = @FILECMD@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OPENSSL_INCLUDES = @OPENSSL_INCLUDES@
+OPENSSL_LDFLAGS = @OPENSSL_LDFLAGS@
+OPENSSL_LIBS = @OPENSSL_LIBS@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+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@
+SHELL = @SHELL@
+STRIP = @STRIP@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+ax_pthread_config = @ax_pthread_config@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+lib_LTLIBRARIES = libiperf.la # Build and install an iperf library
+include_HEADERS = iperf_api.h # Defines the headers that get installed with the program
+
+# Specify the source files and flags for the iperf library
+libiperf_la_SOURCES = \
+ cjson.c \
+ cjson.h \
+ flowlabel.h \
+ iperf.h \
+ iperf_api.c \
+ iperf_api.h \
+ iperf_error.c \
+ iperf_auth.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
+
+
+# Specify the sources and various flags for the iperf binary
+iperf3_SOURCES = main.c
+iperf3_CFLAGS = -g
+iperf3_LDADD = libiperf.la
+iperf3_LDFLAGS = -g
+
+# If the iperf-profiled-binary is enabled
+# Specify the sources and various flags for the profiled iperf binary. This
+# binary recompiles all the source files to make sure they are all profiled.
+@ENABLE_PROFILING_TRUE@iperf3_profile_SOURCES = main.c \
+@ENABLE_PROFILING_TRUE@ $(libiperf_la_SOURCES)
+
+@ENABLE_PROFILING_TRUE@iperf3_profile_CFLAGS = -pg -g
+@ENABLE_PROFILING_TRUE@iperf3_profile_LDADD = libiperf.la
+@ENABLE_PROFILING_TRUE@iperf3_profile_LDFLAGS = -pg -g
+
+# Specify the sources and various flags for the test cases
+t_timer_SOURCES = t_timer.c
+t_timer_CFLAGS = -g
+t_timer_LDFLAGS =
+t_timer_LDADD = libiperf.la
+t_units_SOURCES = t_units.c
+t_units_CFLAGS = -g
+t_units_LDFLAGS =
+t_units_LDADD = libiperf.la
+t_uuid_SOURCES = t_uuid.c
+t_uuid_CFLAGS = -g
+t_uuid_LDFLAGS =
+t_uuid_LDADD = libiperf.la
+t_api_SOURCES = t_api.c
+t_api_CFLAGS = -g
+t_api_LDFLAGS =
+t_api_LDADD = libiperf.la
+t_auth_SOURCES = t_auth.c
+t_auth_CFLAGS = -g
+t_auth_LDFLAGS =
+t_auth_LDADD = libiperf.la
+dist_man_MANS = iperf3.1 libiperf.3
+all: iperf_config.h
+ $(MAKE) $(AM_MAKEFLAGS) all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .log .o .obj .test .test$(EXEEXT) .trs
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign src/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+iperf_config.h: stamp-h1
+ @test -f $@ || rm -f 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
+$(srcdir)/iperf_config.h.in: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ ($(am__cd) $(top_srcdir) && $(AUTOHEADER))
+ rm -f stamp-h1
+ touch $@
+
+distclean-hdr:
+ -rm -f iperf_config.h stamp-h1
+version.h: $(top_builddir)/config.status $(srcdir)/version.h.in
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
+install-binPROGRAMS: $(bin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-binPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(bindir)" && 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
+
+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
+
+install-libLTLIBRARIES: $(lib_LTLIBRARIES)
+ @$(NORMAL_INSTALL)
+ @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+ list2=; for p in $$list; do \
+ if test -f $$p; then \
+ list2="$$list2 $$p"; \
+ else :; fi; \
+ done; \
+ test -z "$$list2" || { \
+ echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \
+ }
+
+uninstall-libLTLIBRARIES:
+ @$(NORMAL_UNINSTALL)
+ @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+ for p in $$list; do \
+ $(am__strip_dir) \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \
+ done
+
+clean-libLTLIBRARIES:
+ -test -z "$(lib_LTLIBRARIES)" || 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}; \
+ }
+
+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)
+
+iperf3$(EXEEXT): $(iperf3_OBJECTS) $(iperf3_DEPENDENCIES) $(EXTRA_iperf3_DEPENDENCIES)
+ @rm -f iperf3$(EXEEXT)
+ $(AM_V_CCLD)$(iperf3_LINK) $(iperf3_OBJECTS) $(iperf3_LDADD) $(LIBS)
+
+iperf3_profile$(EXEEXT): $(iperf3_profile_OBJECTS) $(iperf3_profile_DEPENDENCIES) $(EXTRA_iperf3_profile_DEPENDENCIES)
+ @rm -f iperf3_profile$(EXEEXT)
+ $(AM_V_CCLD)$(iperf3_profile_LINK) $(iperf3_profile_OBJECTS) $(iperf3_profile_LDADD) $(LIBS)
+
+t_api$(EXEEXT): $(t_api_OBJECTS) $(t_api_DEPENDENCIES) $(EXTRA_t_api_DEPENDENCIES)
+ @rm -f t_api$(EXEEXT)
+ $(AM_V_CCLD)$(t_api_LINK) $(t_api_OBJECTS) $(t_api_LDADD) $(LIBS)
+
+t_auth$(EXEEXT): $(t_auth_OBJECTS) $(t_auth_DEPENDENCIES) $(EXTRA_t_auth_DEPENDENCIES)
+ @rm -f t_auth$(EXEEXT)
+ $(AM_V_CCLD)$(t_auth_LINK) $(t_auth_OBJECTS) $(t_auth_LDADD) $(LIBS)
+
+t_timer$(EXEEXT): $(t_timer_OBJECTS) $(t_timer_DEPENDENCIES) $(EXTRA_t_timer_DEPENDENCIES)
+ @rm -f t_timer$(EXEEXT)
+ $(AM_V_CCLD)$(t_timer_LINK) $(t_timer_OBJECTS) $(t_timer_LDADD) $(LIBS)
+
+t_units$(EXEEXT): $(t_units_OBJECTS) $(t_units_DEPENDENCIES) $(EXTRA_t_units_DEPENDENCIES)
+ @rm -f t_units$(EXEEXT)
+ $(AM_V_CCLD)$(t_units_LINK) $(t_units_OBJECTS) $(t_units_LDADD) $(LIBS)
+
+t_uuid$(EXEEXT): $(t_uuid_OBJECTS) $(t_uuid_DEPENDENCIES) $(EXTRA_t_uuid_DEPENDENCIES)
+ @rm -f t_uuid$(EXEEXT)
+ $(AM_V_CCLD)$(t_uuid_LINK) $(t_uuid_OBJECTS) $(t_uuid_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cjson.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dscp.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/iperf3-main.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/iperf3_profile-cjson.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/iperf3_profile-dscp.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/iperf3_profile-iperf_api.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/iperf3_profile-iperf_auth.Po@am__quote@ # am--include-marker
+@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_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
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/iperf3_profile-iperf_time.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/iperf3_profile-iperf_udp.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/iperf3_profile-iperf_util.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/iperf3_profile-main.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/iperf3_profile-net.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/iperf3_profile-tcp_info.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/iperf3_profile-timer.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/iperf3_profile-units.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/iperf_api.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/iperf_auth.Plo@am__quote@ # am--include-marker
+@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_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
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/iperf_time.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/iperf_udp.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/iperf_util.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/net.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t_api-t_api.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t_auth-t_auth.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t_timer-t_timer.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t_units-t_units.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t_uuid-t_uuid.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tcp_info.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/timer.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/units.Plo@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+iperf3-main.o: main.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(iperf3_CFLAGS) $(CFLAGS) -MT iperf3-main.o -MD -MP -MF $(DEPDIR)/iperf3-main.Tpo -c -o iperf3-main.o `test -f 'main.c' || echo '$(srcdir)/'`main.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/iperf3-main.Tpo $(DEPDIR)/iperf3-main.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='main.c' object='iperf3-main.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_CFLAGS) $(CFLAGS) -c -o iperf3-main.o `test -f 'main.c' || echo '$(srcdir)/'`main.c
+
+iperf3-main.obj: main.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(iperf3_CFLAGS) $(CFLAGS) -MT iperf3-main.obj -MD -MP -MF $(DEPDIR)/iperf3-main.Tpo -c -o iperf3-main.obj `if test -f 'main.c'; then $(CYGPATH_W) 'main.c'; else $(CYGPATH_W) '$(srcdir)/main.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/iperf3-main.Tpo $(DEPDIR)/iperf3-main.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='main.c' object='iperf3-main.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_CFLAGS) $(CFLAGS) -c -o iperf3-main.obj `if test -f 'main.c'; then $(CYGPATH_W) 'main.c'; else $(CYGPATH_W) '$(srcdir)/main.c'; fi`
+
+iperf3_profile-main.o: main.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(iperf3_profile_CFLAGS) $(CFLAGS) -MT iperf3_profile-main.o -MD -MP -MF $(DEPDIR)/iperf3_profile-main.Tpo -c -o iperf3_profile-main.o `test -f 'main.c' || echo '$(srcdir)/'`main.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/iperf3_profile-main.Tpo $(DEPDIR)/iperf3_profile-main.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='main.c' object='iperf3_profile-main.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-main.o `test -f 'main.c' || echo '$(srcdir)/'`main.c
+
+iperf3_profile-main.obj: main.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(iperf3_profile_CFLAGS) $(CFLAGS) -MT iperf3_profile-main.obj -MD -MP -MF $(DEPDIR)/iperf3_profile-main.Tpo -c -o iperf3_profile-main.obj `if test -f 'main.c'; then $(CYGPATH_W) 'main.c'; else $(CYGPATH_W) '$(srcdir)/main.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/iperf3_profile-main.Tpo $(DEPDIR)/iperf3_profile-main.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='main.c' object='iperf3_profile-main.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-main.obj `if test -f 'main.c'; then $(CYGPATH_W) 'main.c'; else $(CYGPATH_W) '$(srcdir)/main.c'; fi`
+
+iperf3_profile-cjson.o: cjson.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(iperf3_profile_CFLAGS) $(CFLAGS) -MT iperf3_profile-cjson.o -MD -MP -MF $(DEPDIR)/iperf3_profile-cjson.Tpo -c -o iperf3_profile-cjson.o `test -f 'cjson.c' || echo '$(srcdir)/'`cjson.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/iperf3_profile-cjson.Tpo $(DEPDIR)/iperf3_profile-cjson.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='cjson.c' object='iperf3_profile-cjson.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-cjson.o `test -f 'cjson.c' || echo '$(srcdir)/'`cjson.c
+
+iperf3_profile-cjson.obj: cjson.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(iperf3_profile_CFLAGS) $(CFLAGS) -MT iperf3_profile-cjson.obj -MD -MP -MF $(DEPDIR)/iperf3_profile-cjson.Tpo -c -o iperf3_profile-cjson.obj `if test -f 'cjson.c'; then $(CYGPATH_W) 'cjson.c'; else $(CYGPATH_W) '$(srcdir)/cjson.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/iperf3_profile-cjson.Tpo $(DEPDIR)/iperf3_profile-cjson.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='cjson.c' object='iperf3_profile-cjson.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-cjson.obj `if test -f 'cjson.c'; then $(CYGPATH_W) 'cjson.c'; else $(CYGPATH_W) '$(srcdir)/cjson.c'; fi`
+
+iperf3_profile-iperf_api.o: iperf_api.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(iperf3_profile_CFLAGS) $(CFLAGS) -MT iperf3_profile-iperf_api.o -MD -MP -MF $(DEPDIR)/iperf3_profile-iperf_api.Tpo -c -o iperf3_profile-iperf_api.o `test -f 'iperf_api.c' || echo '$(srcdir)/'`iperf_api.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/iperf3_profile-iperf_api.Tpo $(DEPDIR)/iperf3_profile-iperf_api.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='iperf_api.c' object='iperf3_profile-iperf_api.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_api.o `test -f 'iperf_api.c' || echo '$(srcdir)/'`iperf_api.c
+
+iperf3_profile-iperf_api.obj: iperf_api.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(iperf3_profile_CFLAGS) $(CFLAGS) -MT iperf3_profile-iperf_api.obj -MD -MP -MF $(DEPDIR)/iperf3_profile-iperf_api.Tpo -c -o iperf3_profile-iperf_api.obj `if test -f 'iperf_api.c'; then $(CYGPATH_W) 'iperf_api.c'; else $(CYGPATH_W) '$(srcdir)/iperf_api.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/iperf3_profile-iperf_api.Tpo $(DEPDIR)/iperf3_profile-iperf_api.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='iperf_api.c' object='iperf3_profile-iperf_api.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_api.obj `if test -f 'iperf_api.c'; then $(CYGPATH_W) 'iperf_api.c'; else $(CYGPATH_W) '$(srcdir)/iperf_api.c'; fi`
+
+iperf3_profile-iperf_error.o: iperf_error.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(iperf3_profile_CFLAGS) $(CFLAGS) -MT iperf3_profile-iperf_error.o -MD -MP -MF $(DEPDIR)/iperf3_profile-iperf_error.Tpo -c -o iperf3_profile-iperf_error.o `test -f 'iperf_error.c' || echo '$(srcdir)/'`iperf_error.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/iperf3_profile-iperf_error.Tpo $(DEPDIR)/iperf3_profile-iperf_error.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='iperf_error.c' object='iperf3_profile-iperf_error.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_error.o `test -f 'iperf_error.c' || echo '$(srcdir)/'`iperf_error.c
+
+iperf3_profile-iperf_error.obj: iperf_error.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(iperf3_profile_CFLAGS) $(CFLAGS) -MT iperf3_profile-iperf_error.obj -MD -MP -MF $(DEPDIR)/iperf3_profile-iperf_error.Tpo -c -o iperf3_profile-iperf_error.obj `if test -f 'iperf_error.c'; then $(CYGPATH_W) 'iperf_error.c'; else $(CYGPATH_W) '$(srcdir)/iperf_error.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/iperf3_profile-iperf_error.Tpo $(DEPDIR)/iperf3_profile-iperf_error.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='iperf_error.c' object='iperf3_profile-iperf_error.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_error.obj `if test -f 'iperf_error.c'; then $(CYGPATH_W) 'iperf_error.c'; else $(CYGPATH_W) '$(srcdir)/iperf_error.c'; fi`
+
+iperf3_profile-iperf_auth.o: iperf_auth.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(iperf3_profile_CFLAGS) $(CFLAGS) -MT iperf3_profile-iperf_auth.o -MD -MP -MF $(DEPDIR)/iperf3_profile-iperf_auth.Tpo -c -o iperf3_profile-iperf_auth.o `test -f 'iperf_auth.c' || echo '$(srcdir)/'`iperf_auth.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/iperf3_profile-iperf_auth.Tpo $(DEPDIR)/iperf3_profile-iperf_auth.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='iperf_auth.c' object='iperf3_profile-iperf_auth.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_auth.o `test -f 'iperf_auth.c' || echo '$(srcdir)/'`iperf_auth.c
+
+iperf3_profile-iperf_auth.obj: iperf_auth.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(iperf3_profile_CFLAGS) $(CFLAGS) -MT iperf3_profile-iperf_auth.obj -MD -MP -MF $(DEPDIR)/iperf3_profile-iperf_auth.Tpo -c -o iperf3_profile-iperf_auth.obj `if test -f 'iperf_auth.c'; then $(CYGPATH_W) 'iperf_auth.c'; else $(CYGPATH_W) '$(srcdir)/iperf_auth.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/iperf3_profile-iperf_auth.Tpo $(DEPDIR)/iperf3_profile-iperf_auth.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='iperf_auth.c' object='iperf3_profile-iperf_auth.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_auth.obj `if test -f 'iperf_auth.c'; then $(CYGPATH_W) 'iperf_auth.c'; else $(CYGPATH_W) '$(srcdir)/iperf_auth.c'; fi`
+
+iperf3_profile-iperf_client_api.o: iperf_client_api.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(iperf3_profile_CFLAGS) $(CFLAGS) -MT iperf3_profile-iperf_client_api.o -MD -MP -MF $(DEPDIR)/iperf3_profile-iperf_client_api.Tpo -c -o iperf3_profile-iperf_client_api.o `test -f 'iperf_client_api.c' || echo '$(srcdir)/'`iperf_client_api.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/iperf3_profile-iperf_client_api.Tpo $(DEPDIR)/iperf3_profile-iperf_client_api.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='iperf_client_api.c' object='iperf3_profile-iperf_client_api.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_client_api.o `test -f 'iperf_client_api.c' || echo '$(srcdir)/'`iperf_client_api.c
+
+iperf3_profile-iperf_client_api.obj: iperf_client_api.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(iperf3_profile_CFLAGS) $(CFLAGS) -MT iperf3_profile-iperf_client_api.obj -MD -MP -MF $(DEPDIR)/iperf3_profile-iperf_client_api.Tpo -c -o iperf3_profile-iperf_client_api.obj `if test -f 'iperf_client_api.c'; then $(CYGPATH_W) 'iperf_client_api.c'; else $(CYGPATH_W) '$(srcdir)/iperf_client_api.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/iperf3_profile-iperf_client_api.Tpo $(DEPDIR)/iperf3_profile-iperf_client_api.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='iperf_client_api.c' object='iperf3_profile-iperf_client_api.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_client_api.obj `if test -f 'iperf_client_api.c'; then $(CYGPATH_W) 'iperf_client_api.c'; else $(CYGPATH_W) '$(srcdir)/iperf_client_api.c'; fi`
+
+iperf3_profile-iperf_locale.o: iperf_locale.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(iperf3_profile_CFLAGS) $(CFLAGS) -MT iperf3_profile-iperf_locale.o -MD -MP -MF $(DEPDIR)/iperf3_profile-iperf_locale.Tpo -c -o iperf3_profile-iperf_locale.o `test -f 'iperf_locale.c' || echo '$(srcdir)/'`iperf_locale.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/iperf3_profile-iperf_locale.Tpo $(DEPDIR)/iperf3_profile-iperf_locale.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='iperf_locale.c' object='iperf3_profile-iperf_locale.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_locale.o `test -f 'iperf_locale.c' || echo '$(srcdir)/'`iperf_locale.c
+
+iperf3_profile-iperf_locale.obj: iperf_locale.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(iperf3_profile_CFLAGS) $(CFLAGS) -MT iperf3_profile-iperf_locale.obj -MD -MP -MF $(DEPDIR)/iperf3_profile-iperf_locale.Tpo -c -o iperf3_profile-iperf_locale.obj `if test -f 'iperf_locale.c'; then $(CYGPATH_W) 'iperf_locale.c'; else $(CYGPATH_W) '$(srcdir)/iperf_locale.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/iperf3_profile-iperf_locale.Tpo $(DEPDIR)/iperf3_profile-iperf_locale.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='iperf_locale.c' object='iperf3_profile-iperf_locale.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_locale.obj `if test -f 'iperf_locale.c'; then $(CYGPATH_W) 'iperf_locale.c'; else $(CYGPATH_W) '$(srcdir)/iperf_locale.c'; fi`
+
+iperf3_profile-iperf_server_api.o: iperf_server_api.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(iperf3_profile_CFLAGS) $(CFLAGS) -MT iperf3_profile-iperf_server_api.o -MD -MP -MF $(DEPDIR)/iperf3_profile-iperf_server_api.Tpo -c -o iperf3_profile-iperf_server_api.o `test -f 'iperf_server_api.c' || echo '$(srcdir)/'`iperf_server_api.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/iperf3_profile-iperf_server_api.Tpo $(DEPDIR)/iperf3_profile-iperf_server_api.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='iperf_server_api.c' object='iperf3_profile-iperf_server_api.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_server_api.o `test -f 'iperf_server_api.c' || echo '$(srcdir)/'`iperf_server_api.c
+
+iperf3_profile-iperf_server_api.obj: iperf_server_api.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(iperf3_profile_CFLAGS) $(CFLAGS) -MT iperf3_profile-iperf_server_api.obj -MD -MP -MF $(DEPDIR)/iperf3_profile-iperf_server_api.Tpo -c -o iperf3_profile-iperf_server_api.obj `if test -f 'iperf_server_api.c'; then $(CYGPATH_W) 'iperf_server_api.c'; else $(CYGPATH_W) '$(srcdir)/iperf_server_api.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/iperf3_profile-iperf_server_api.Tpo $(DEPDIR)/iperf3_profile-iperf_server_api.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='iperf_server_api.c' object='iperf3_profile-iperf_server_api.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_server_api.obj `if test -f 'iperf_server_api.c'; then $(CYGPATH_W) 'iperf_server_api.c'; else $(CYGPATH_W) '$(srcdir)/iperf_server_api.c'; fi`
+
+iperf3_profile-iperf_tcp.o: iperf_tcp.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(iperf3_profile_CFLAGS) $(CFLAGS) -MT iperf3_profile-iperf_tcp.o -MD -MP -MF $(DEPDIR)/iperf3_profile-iperf_tcp.Tpo -c -o iperf3_profile-iperf_tcp.o `test -f 'iperf_tcp.c' || echo '$(srcdir)/'`iperf_tcp.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/iperf3_profile-iperf_tcp.Tpo $(DEPDIR)/iperf3_profile-iperf_tcp.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='iperf_tcp.c' object='iperf3_profile-iperf_tcp.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_tcp.o `test -f 'iperf_tcp.c' || echo '$(srcdir)/'`iperf_tcp.c
+
+iperf3_profile-iperf_tcp.obj: iperf_tcp.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(iperf3_profile_CFLAGS) $(CFLAGS) -MT iperf3_profile-iperf_tcp.obj -MD -MP -MF $(DEPDIR)/iperf3_profile-iperf_tcp.Tpo -c -o iperf3_profile-iperf_tcp.obj `if test -f 'iperf_tcp.c'; then $(CYGPATH_W) 'iperf_tcp.c'; else $(CYGPATH_W) '$(srcdir)/iperf_tcp.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/iperf3_profile-iperf_tcp.Tpo $(DEPDIR)/iperf3_profile-iperf_tcp.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='iperf_tcp.c' object='iperf3_profile-iperf_tcp.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_tcp.obj `if test -f 'iperf_tcp.c'; then $(CYGPATH_W) 'iperf_tcp.c'; else $(CYGPATH_W) '$(srcdir)/iperf_tcp.c'; fi`
+
+iperf3_profile-iperf_udp.o: iperf_udp.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(iperf3_profile_CFLAGS) $(CFLAGS) -MT iperf3_profile-iperf_udp.o -MD -MP -MF $(DEPDIR)/iperf3_profile-iperf_udp.Tpo -c -o iperf3_profile-iperf_udp.o `test -f 'iperf_udp.c' || echo '$(srcdir)/'`iperf_udp.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/iperf3_profile-iperf_udp.Tpo $(DEPDIR)/iperf3_profile-iperf_udp.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='iperf_udp.c' object='iperf3_profile-iperf_udp.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_udp.o `test -f 'iperf_udp.c' || echo '$(srcdir)/'`iperf_udp.c
+
+iperf3_profile-iperf_udp.obj: iperf_udp.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(iperf3_profile_CFLAGS) $(CFLAGS) -MT iperf3_profile-iperf_udp.obj -MD -MP -MF $(DEPDIR)/iperf3_profile-iperf_udp.Tpo -c -o iperf3_profile-iperf_udp.obj `if test -f 'iperf_udp.c'; then $(CYGPATH_W) 'iperf_udp.c'; else $(CYGPATH_W) '$(srcdir)/iperf_udp.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/iperf3_profile-iperf_udp.Tpo $(DEPDIR)/iperf3_profile-iperf_udp.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='iperf_udp.c' object='iperf3_profile-iperf_udp.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_udp.obj `if test -f 'iperf_udp.c'; then $(CYGPATH_W) 'iperf_udp.c'; else $(CYGPATH_W) '$(srcdir)/iperf_udp.c'; fi`
+
+iperf3_profile-iperf_sctp.o: iperf_sctp.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(iperf3_profile_CFLAGS) $(CFLAGS) -MT iperf3_profile-iperf_sctp.o -MD -MP -MF $(DEPDIR)/iperf3_profile-iperf_sctp.Tpo -c -o iperf3_profile-iperf_sctp.o `test -f 'iperf_sctp.c' || echo '$(srcdir)/'`iperf_sctp.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/iperf3_profile-iperf_sctp.Tpo $(DEPDIR)/iperf3_profile-iperf_sctp.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='iperf_sctp.c' object='iperf3_profile-iperf_sctp.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_sctp.o `test -f 'iperf_sctp.c' || echo '$(srcdir)/'`iperf_sctp.c
+
+iperf3_profile-iperf_sctp.obj: iperf_sctp.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(iperf3_profile_CFLAGS) $(CFLAGS) -MT iperf3_profile-iperf_sctp.obj -MD -MP -MF $(DEPDIR)/iperf3_profile-iperf_sctp.Tpo -c -o iperf3_profile-iperf_sctp.obj `if test -f 'iperf_sctp.c'; then $(CYGPATH_W) 'iperf_sctp.c'; else $(CYGPATH_W) '$(srcdir)/iperf_sctp.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/iperf3_profile-iperf_sctp.Tpo $(DEPDIR)/iperf3_profile-iperf_sctp.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='iperf_sctp.c' object='iperf3_profile-iperf_sctp.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_sctp.obj `if test -f 'iperf_sctp.c'; then $(CYGPATH_W) 'iperf_sctp.c'; else $(CYGPATH_W) '$(srcdir)/iperf_sctp.c'; fi`
+
+iperf3_profile-iperf_util.o: iperf_util.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(iperf3_profile_CFLAGS) $(CFLAGS) -MT iperf3_profile-iperf_util.o -MD -MP -MF $(DEPDIR)/iperf3_profile-iperf_util.Tpo -c -o iperf3_profile-iperf_util.o `test -f 'iperf_util.c' || echo '$(srcdir)/'`iperf_util.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/iperf3_profile-iperf_util.Tpo $(DEPDIR)/iperf3_profile-iperf_util.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='iperf_util.c' object='iperf3_profile-iperf_util.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_util.o `test -f 'iperf_util.c' || echo '$(srcdir)/'`iperf_util.c
+
+iperf3_profile-iperf_util.obj: iperf_util.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(iperf3_profile_CFLAGS) $(CFLAGS) -MT iperf3_profile-iperf_util.obj -MD -MP -MF $(DEPDIR)/iperf3_profile-iperf_util.Tpo -c -o iperf3_profile-iperf_util.obj `if test -f 'iperf_util.c'; then $(CYGPATH_W) 'iperf_util.c'; else $(CYGPATH_W) '$(srcdir)/iperf_util.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/iperf3_profile-iperf_util.Tpo $(DEPDIR)/iperf3_profile-iperf_util.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='iperf_util.c' object='iperf3_profile-iperf_util.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_util.obj `if test -f 'iperf_util.c'; then $(CYGPATH_W) 'iperf_util.c'; else $(CYGPATH_W) '$(srcdir)/iperf_util.c'; fi`
+
+iperf3_profile-iperf_time.o: iperf_time.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(iperf3_profile_CFLAGS) $(CFLAGS) -MT iperf3_profile-iperf_time.o -MD -MP -MF $(DEPDIR)/iperf3_profile-iperf_time.Tpo -c -o iperf3_profile-iperf_time.o `test -f 'iperf_time.c' || echo '$(srcdir)/'`iperf_time.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/iperf3_profile-iperf_time.Tpo $(DEPDIR)/iperf3_profile-iperf_time.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='iperf_time.c' object='iperf3_profile-iperf_time.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_time.o `test -f 'iperf_time.c' || echo '$(srcdir)/'`iperf_time.c
+
+iperf3_profile-iperf_time.obj: iperf_time.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(iperf3_profile_CFLAGS) $(CFLAGS) -MT iperf3_profile-iperf_time.obj -MD -MP -MF $(DEPDIR)/iperf3_profile-iperf_time.Tpo -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`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/iperf3_profile-iperf_time.Tpo $(DEPDIR)/iperf3_profile-iperf_time.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='iperf_time.c' object='iperf3_profile-iperf_time.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_time.obj `if test -f 'iperf_time.c'; then $(CYGPATH_W) 'iperf_time.c'; else $(CYGPATH_W) '$(srcdir)/iperf_time.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
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dscp.c' object='iperf3_profile-dscp.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-dscp.o `test -f 'dscp.c' || echo '$(srcdir)/'`dscp.c
+
+iperf3_profile-dscp.obj: dscp.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(iperf3_profile_CFLAGS) $(CFLAGS) -MT iperf3_profile-dscp.obj -MD -MP -MF $(DEPDIR)/iperf3_profile-dscp.Tpo -c -o iperf3_profile-dscp.obj `if test -f 'dscp.c'; then $(CYGPATH_W) 'dscp.c'; else $(CYGPATH_W) '$(srcdir)/dscp.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/iperf3_profile-dscp.Tpo $(DEPDIR)/iperf3_profile-dscp.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dscp.c' object='iperf3_profile-dscp.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-dscp.obj `if test -f 'dscp.c'; then $(CYGPATH_W) 'dscp.c'; else $(CYGPATH_W) '$(srcdir)/dscp.c'; fi`
+
+iperf3_profile-net.o: net.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(iperf3_profile_CFLAGS) $(CFLAGS) -MT iperf3_profile-net.o -MD -MP -MF $(DEPDIR)/iperf3_profile-net.Tpo -c -o iperf3_profile-net.o `test -f 'net.c' || echo '$(srcdir)/'`net.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/iperf3_profile-net.Tpo $(DEPDIR)/iperf3_profile-net.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='net.c' object='iperf3_profile-net.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-net.o `test -f 'net.c' || echo '$(srcdir)/'`net.c
+
+iperf3_profile-net.obj: net.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(iperf3_profile_CFLAGS) $(CFLAGS) -MT iperf3_profile-net.obj -MD -MP -MF $(DEPDIR)/iperf3_profile-net.Tpo -c -o iperf3_profile-net.obj `if test -f 'net.c'; then $(CYGPATH_W) 'net.c'; else $(CYGPATH_W) '$(srcdir)/net.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/iperf3_profile-net.Tpo $(DEPDIR)/iperf3_profile-net.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='net.c' object='iperf3_profile-net.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-net.obj `if test -f 'net.c'; then $(CYGPATH_W) 'net.c'; else $(CYGPATH_W) '$(srcdir)/net.c'; fi`
+
+iperf3_profile-tcp_info.o: tcp_info.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(iperf3_profile_CFLAGS) $(CFLAGS) -MT iperf3_profile-tcp_info.o -MD -MP -MF $(DEPDIR)/iperf3_profile-tcp_info.Tpo -c -o iperf3_profile-tcp_info.o `test -f 'tcp_info.c' || echo '$(srcdir)/'`tcp_info.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/iperf3_profile-tcp_info.Tpo $(DEPDIR)/iperf3_profile-tcp_info.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tcp_info.c' object='iperf3_profile-tcp_info.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-tcp_info.o `test -f 'tcp_info.c' || echo '$(srcdir)/'`tcp_info.c
+
+iperf3_profile-tcp_info.obj: tcp_info.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(iperf3_profile_CFLAGS) $(CFLAGS) -MT iperf3_profile-tcp_info.obj -MD -MP -MF $(DEPDIR)/iperf3_profile-tcp_info.Tpo -c -o iperf3_profile-tcp_info.obj `if test -f 'tcp_info.c'; then $(CYGPATH_W) 'tcp_info.c'; else $(CYGPATH_W) '$(srcdir)/tcp_info.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/iperf3_profile-tcp_info.Tpo $(DEPDIR)/iperf3_profile-tcp_info.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tcp_info.c' object='iperf3_profile-tcp_info.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-tcp_info.obj `if test -f 'tcp_info.c'; then $(CYGPATH_W) 'tcp_info.c'; else $(CYGPATH_W) '$(srcdir)/tcp_info.c'; fi`
+
+iperf3_profile-timer.o: timer.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(iperf3_profile_CFLAGS) $(CFLAGS) -MT iperf3_profile-timer.o -MD -MP -MF $(DEPDIR)/iperf3_profile-timer.Tpo -c -o iperf3_profile-timer.o `test -f 'timer.c' || echo '$(srcdir)/'`timer.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/iperf3_profile-timer.Tpo $(DEPDIR)/iperf3_profile-timer.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='timer.c' object='iperf3_profile-timer.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-timer.o `test -f 'timer.c' || echo '$(srcdir)/'`timer.c
+
+iperf3_profile-timer.obj: timer.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(iperf3_profile_CFLAGS) $(CFLAGS) -MT iperf3_profile-timer.obj -MD -MP -MF $(DEPDIR)/iperf3_profile-timer.Tpo -c -o iperf3_profile-timer.obj `if test -f 'timer.c'; then $(CYGPATH_W) 'timer.c'; else $(CYGPATH_W) '$(srcdir)/timer.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/iperf3_profile-timer.Tpo $(DEPDIR)/iperf3_profile-timer.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='timer.c' object='iperf3_profile-timer.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-timer.obj `if test -f 'timer.c'; then $(CYGPATH_W) 'timer.c'; else $(CYGPATH_W) '$(srcdir)/timer.c'; fi`
+
+iperf3_profile-units.o: units.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(iperf3_profile_CFLAGS) $(CFLAGS) -MT iperf3_profile-units.o -MD -MP -MF $(DEPDIR)/iperf3_profile-units.Tpo -c -o iperf3_profile-units.o `test -f 'units.c' || echo '$(srcdir)/'`units.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/iperf3_profile-units.Tpo $(DEPDIR)/iperf3_profile-units.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='units.c' object='iperf3_profile-units.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-units.o `test -f 'units.c' || echo '$(srcdir)/'`units.c
+
+iperf3_profile-units.obj: units.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(iperf3_profile_CFLAGS) $(CFLAGS) -MT iperf3_profile-units.obj -MD -MP -MF $(DEPDIR)/iperf3_profile-units.Tpo -c -o iperf3_profile-units.obj `if test -f 'units.c'; then $(CYGPATH_W) 'units.c'; else $(CYGPATH_W) '$(srcdir)/units.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/iperf3_profile-units.Tpo $(DEPDIR)/iperf3_profile-units.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='units.c' object='iperf3_profile-units.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-units.obj `if test -f 'units.c'; then $(CYGPATH_W) 'units.c'; else $(CYGPATH_W) '$(srcdir)/units.c'; fi`
+
+t_api-t_api.o: t_api.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(t_api_CFLAGS) $(CFLAGS) -MT t_api-t_api.o -MD -MP -MF $(DEPDIR)/t_api-t_api.Tpo -c -o t_api-t_api.o `test -f 't_api.c' || echo '$(srcdir)/'`t_api.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/t_api-t_api.Tpo $(DEPDIR)/t_api-t_api.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='t_api.c' object='t_api-t_api.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) $(t_api_CFLAGS) $(CFLAGS) -c -o t_api-t_api.o `test -f 't_api.c' || echo '$(srcdir)/'`t_api.c
+
+t_api-t_api.obj: t_api.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(t_api_CFLAGS) $(CFLAGS) -MT t_api-t_api.obj -MD -MP -MF $(DEPDIR)/t_api-t_api.Tpo -c -o t_api-t_api.obj `if test -f 't_api.c'; then $(CYGPATH_W) 't_api.c'; else $(CYGPATH_W) '$(srcdir)/t_api.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/t_api-t_api.Tpo $(DEPDIR)/t_api-t_api.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='t_api.c' object='t_api-t_api.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) $(t_api_CFLAGS) $(CFLAGS) -c -o t_api-t_api.obj `if test -f 't_api.c'; then $(CYGPATH_W) 't_api.c'; else $(CYGPATH_W) '$(srcdir)/t_api.c'; fi`
+
+t_auth-t_auth.o: t_auth.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(t_auth_CFLAGS) $(CFLAGS) -MT t_auth-t_auth.o -MD -MP -MF $(DEPDIR)/t_auth-t_auth.Tpo -c -o t_auth-t_auth.o `test -f 't_auth.c' || echo '$(srcdir)/'`t_auth.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/t_auth-t_auth.Tpo $(DEPDIR)/t_auth-t_auth.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='t_auth.c' object='t_auth-t_auth.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) $(t_auth_CFLAGS) $(CFLAGS) -c -o t_auth-t_auth.o `test -f 't_auth.c' || echo '$(srcdir)/'`t_auth.c
+
+t_auth-t_auth.obj: t_auth.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(t_auth_CFLAGS) $(CFLAGS) -MT t_auth-t_auth.obj -MD -MP -MF $(DEPDIR)/t_auth-t_auth.Tpo -c -o t_auth-t_auth.obj `if test -f 't_auth.c'; then $(CYGPATH_W) 't_auth.c'; else $(CYGPATH_W) '$(srcdir)/t_auth.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/t_auth-t_auth.Tpo $(DEPDIR)/t_auth-t_auth.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='t_auth.c' object='t_auth-t_auth.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) $(t_auth_CFLAGS) $(CFLAGS) -c -o t_auth-t_auth.obj `if test -f 't_auth.c'; then $(CYGPATH_W) 't_auth.c'; else $(CYGPATH_W) '$(srcdir)/t_auth.c'; fi`
+
+t_timer-t_timer.o: t_timer.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(t_timer_CFLAGS) $(CFLAGS) -MT t_timer-t_timer.o -MD -MP -MF $(DEPDIR)/t_timer-t_timer.Tpo -c -o t_timer-t_timer.o `test -f 't_timer.c' || echo '$(srcdir)/'`t_timer.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/t_timer-t_timer.Tpo $(DEPDIR)/t_timer-t_timer.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='t_timer.c' object='t_timer-t_timer.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) $(t_timer_CFLAGS) $(CFLAGS) -c -o t_timer-t_timer.o `test -f 't_timer.c' || echo '$(srcdir)/'`t_timer.c
+
+t_timer-t_timer.obj: t_timer.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(t_timer_CFLAGS) $(CFLAGS) -MT t_timer-t_timer.obj -MD -MP -MF $(DEPDIR)/t_timer-t_timer.Tpo -c -o t_timer-t_timer.obj `if test -f 't_timer.c'; then $(CYGPATH_W) 't_timer.c'; else $(CYGPATH_W) '$(srcdir)/t_timer.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/t_timer-t_timer.Tpo $(DEPDIR)/t_timer-t_timer.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='t_timer.c' object='t_timer-t_timer.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) $(t_timer_CFLAGS) $(CFLAGS) -c -o t_timer-t_timer.obj `if test -f 't_timer.c'; then $(CYGPATH_W) 't_timer.c'; else $(CYGPATH_W) '$(srcdir)/t_timer.c'; fi`
+
+t_units-t_units.o: t_units.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(t_units_CFLAGS) $(CFLAGS) -MT t_units-t_units.o -MD -MP -MF $(DEPDIR)/t_units-t_units.Tpo -c -o t_units-t_units.o `test -f 't_units.c' || echo '$(srcdir)/'`t_units.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/t_units-t_units.Tpo $(DEPDIR)/t_units-t_units.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='t_units.c' object='t_units-t_units.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) $(t_units_CFLAGS) $(CFLAGS) -c -o t_units-t_units.o `test -f 't_units.c' || echo '$(srcdir)/'`t_units.c
+
+t_units-t_units.obj: t_units.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(t_units_CFLAGS) $(CFLAGS) -MT t_units-t_units.obj -MD -MP -MF $(DEPDIR)/t_units-t_units.Tpo -c -o t_units-t_units.obj `if test -f 't_units.c'; then $(CYGPATH_W) 't_units.c'; else $(CYGPATH_W) '$(srcdir)/t_units.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/t_units-t_units.Tpo $(DEPDIR)/t_units-t_units.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='t_units.c' object='t_units-t_units.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) $(t_units_CFLAGS) $(CFLAGS) -c -o t_units-t_units.obj `if test -f 't_units.c'; then $(CYGPATH_W) 't_units.c'; else $(CYGPATH_W) '$(srcdir)/t_units.c'; fi`
+
+t_uuid-t_uuid.o: t_uuid.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(t_uuid_CFLAGS) $(CFLAGS) -MT t_uuid-t_uuid.o -MD -MP -MF $(DEPDIR)/t_uuid-t_uuid.Tpo -c -o t_uuid-t_uuid.o `test -f 't_uuid.c' || echo '$(srcdir)/'`t_uuid.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/t_uuid-t_uuid.Tpo $(DEPDIR)/t_uuid-t_uuid.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='t_uuid.c' object='t_uuid-t_uuid.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) $(t_uuid_CFLAGS) $(CFLAGS) -c -o t_uuid-t_uuid.o `test -f 't_uuid.c' || echo '$(srcdir)/'`t_uuid.c
+
+t_uuid-t_uuid.obj: t_uuid.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(t_uuid_CFLAGS) $(CFLAGS) -MT t_uuid-t_uuid.obj -MD -MP -MF $(DEPDIR)/t_uuid-t_uuid.Tpo -c -o t_uuid-t_uuid.obj `if test -f 't_uuid.c'; then $(CYGPATH_W) 't_uuid.c'; else $(CYGPATH_W) '$(srcdir)/t_uuid.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/t_uuid-t_uuid.Tpo $(DEPDIR)/t_uuid-t_uuid.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='t_uuid.c' object='t_uuid-t_uuid.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) $(t_uuid_CFLAGS) $(CFLAGS) -c -o t_uuid-t_uuid.obj `if test -f 't_uuid.c'; then $(CYGPATH_W) 't_uuid.c'; else $(CYGPATH_W) '$(srcdir)/t_uuid.c'; fi`
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+install-man1: $(dist_man_MANS)
+ @$(NORMAL_INSTALL)
+ @list1=''; \
+ list2='$(dist_man_MANS)'; \
+ test -n "$(man1dir)" \
+ && test -n "`echo $$list1$$list2`" \
+ || exit 0; \
+ echo " $(MKDIR_P) '$(DESTDIR)$(man1dir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(man1dir)" || exit 1; \
+ { for i in $$list1; do echo "$$i"; done; \
+ if test -n "$$list2"; then \
+ for i in $$list2; do echo "$$i"; done \
+ | sed -n '/\.1[a-z]*$$/p'; \
+ fi; \
+ } | while read p; do \
+ if test -f $$p; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; echo "$$p"; \
+ done | \
+ sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^1][0-9a-z]*$$,1,;x' \
+ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \
+ sed 'N;N;s,\n, ,g' | { \
+ list=; while read file base inst; do \
+ if test "$$base" = "$$inst"; then list="$$list $$file"; else \
+ echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man1dir)/$$inst'"; \
+ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man1dir)/$$inst" || exit $$?; \
+ fi; \
+ done; \
+ for i in $$list; do echo "$$i"; done | $(am__base_list) | \
+ while read files; do \
+ test -z "$$files" || { \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man1dir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(man1dir)" || exit $$?; }; \
+ done; }
+
+uninstall-man1:
+ @$(NORMAL_UNINSTALL)
+ @list=''; test -n "$(man1dir)" || exit 0; \
+ files=`{ for i in $$list; do echo "$$i"; done; \
+ l2='$(dist_man_MANS)'; for i in $$l2; do echo "$$i"; done | \
+ sed -n '/\.1[a-z]*$$/p'; \
+ } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^1][0-9a-z]*$$,1,;x' \
+ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \
+ dir='$(DESTDIR)$(man1dir)'; $(am__uninstall_files_from_dir)
+install-man3: $(dist_man_MANS)
+ @$(NORMAL_INSTALL)
+ @list1=''; \
+ list2='$(dist_man_MANS)'; \
+ test -n "$(man3dir)" \
+ && test -n "`echo $$list1$$list2`" \
+ || exit 0; \
+ echo " $(MKDIR_P) '$(DESTDIR)$(man3dir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(man3dir)" || exit 1; \
+ { for i in $$list1; do echo "$$i"; done; \
+ if test -n "$$list2"; then \
+ for i in $$list2; do echo "$$i"; done \
+ | sed -n '/\.3[a-z]*$$/p'; \
+ fi; \
+ } | while read p; do \
+ if test -f $$p; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; echo "$$p"; \
+ done | \
+ sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^3][0-9a-z]*$$,3,;x' \
+ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \
+ sed 'N;N;s,\n, ,g' | { \
+ list=; while read file base inst; do \
+ if test "$$base" = "$$inst"; then list="$$list $$file"; else \
+ echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man3dir)/$$inst'"; \
+ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man3dir)/$$inst" || exit $$?; \
+ fi; \
+ done; \
+ for i in $$list; do echo "$$i"; done | $(am__base_list) | \
+ while read files; do \
+ test -z "$$files" || { \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man3dir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(man3dir)" || exit $$?; }; \
+ done; }
+
+uninstall-man3:
+ @$(NORMAL_UNINSTALL)
+ @list=''; test -n "$(man3dir)" || exit 0; \
+ files=`{ for i in $$list; do echo "$$i"; done; \
+ l2='$(dist_man_MANS)'; for i in $$l2; do echo "$$i"; done | \
+ sed -n '/\.3[a-z]*$$/p'; \
+ } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^3][0-9a-z]*$$,3,;x' \
+ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \
+ dir='$(DESTDIR)$(man3dir)'; $(am__uninstall_files_from_dir)
+install-includeHEADERS: $(include_HEADERS)
+ @$(NORMAL_INSTALL)
+ @list='$(include_HEADERS)'; test -n "$(includedir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(includedir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(includedir)" || exit 1; \
+ fi; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(includedir)'"; \
+ $(INSTALL_HEADER) $$files "$(DESTDIR)$(includedir)" || exit $$?; \
+ done
+
+uninstall-includeHEADERS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(include_HEADERS)'; test -n "$(includedir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ dir='$(DESTDIR)$(includedir)'; $(am__uninstall_files_from_dir)
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+# Recover from deleted '.trs' file; this should ensure that
+# "rm -f foo.log; make foo.trs" re-run 'foo.test', and re-create
+# both 'foo.log' and 'foo.trs'. Break the recipe in two subshells
+# to avoid problems with "make -n".
+.log.trs:
+ rm -f $< $@
+ $(MAKE) $(AM_MAKEFLAGS) $<
+
+# Leading 'am--fnord' is there to ensure the list of targets does not
+# expand to empty, as could happen e.g. with make check TESTS=''.
+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"; }; \
+ redo_bases=`for i in $$bases; do \
+ am__f_ok $$i.trs && am__f_ok $$i.log || echo $$i; \
+ done`; \
+ if test -n "$$redo_bases"; then \
+ redo_logs=`for i in $$redo_bases; do echo $$i.log; done`; \
+ redo_results=`for i in $$redo_bases; do echo $$i.trs; done`; \
+ if $(am__make_dryrun); then :; else \
+ rm -f $$redo_logs && rm -f $$redo_results || exit 1; \
+ fi; \
+ fi; \
+ if test -n "$$am__remaking_logs"; then \
+ echo "fatal: making $(TEST_SUITE_LOG): possible infinite" \
+ "recursion detected" >&2; \
+ elif test -n "$$redo_logs"; then \
+ am__remaking_logs=yes $(MAKE) $(AM_MAKEFLAGS) $$redo_logs; \
+ fi; \
+ if $(am__make_dryrun); then :; else \
+ st=0; \
+ errmsg="fatal: making $(TEST_SUITE_LOG): failed to create"; \
+ for i in $$redo_bases; do \
+ test -f $$i.trs && test -r $$i.trs \
+ || { echo "$$errmsg $$i.trs" >&2; st=1; }; \
+ test -f $$i.log && test -r $$i.log \
+ || { echo "$$errmsg $$i.log" >&2; st=1; }; \
+ done; \
+ test $$st -eq 0 || exit 1; \
+ fi
+ @$(am__sh_e_setup); $(am__tty_colors); $(am__set_TESTS_bases); \
+ ws='[ ]'; \
+ results=`for b in $$bases; do echo $$b.trs; done`; \
+ test -n "$$results" || results=/dev/null; \
+ all=` grep "^$$ws*:test-result:" $$results | wc -l`; \
+ pass=` grep "^$$ws*:test-result:$$ws*PASS" $$results | wc -l`; \
+ fail=` grep "^$$ws*:test-result:$$ws*FAIL" $$results | wc -l`; \
+ skip=` grep "^$$ws*:test-result:$$ws*SKIP" $$results | wc -l`; \
+ xfail=`grep "^$$ws*:test-result:$$ws*XFAIL" $$results | wc -l`; \
+ xpass=`grep "^$$ws*:test-result:$$ws*XPASS" $$results | wc -l`; \
+ error=`grep "^$$ws*:test-result:$$ws*ERROR" $$results | wc -l`; \
+ if test `expr $$fail + $$xpass + $$error` -eq 0; then \
+ success=true; \
+ else \
+ success=false; \
+ fi; \
+ br='==================='; br=$$br$$br$$br$$br; \
+ result_count () \
+ { \
+ if test x"$$1" = x"--maybe-color"; then \
+ maybe_colorize=yes; \
+ elif test x"$$1" = x"--no-color"; then \
+ maybe_colorize=no; \
+ else \
+ echo "$@: invalid 'result_count' usage" >&2; exit 4; \
+ fi; \
+ shift; \
+ desc=$$1 count=$$2; \
+ if test $$maybe_colorize = yes && test $$count -gt 0; then \
+ color_start=$$3 color_end=$$std; \
+ else \
+ color_start= color_end=; \
+ fi; \
+ echo "$${color_start}# $$desc $$count$${color_end}"; \
+ }; \
+ create_testsuite_report () \
+ { \
+ result_count $$1 "TOTAL:" $$all "$$brg"; \
+ result_count $$1 "PASS: " $$pass "$$grn"; \
+ result_count $$1 "SKIP: " $$skip "$$blu"; \
+ result_count $$1 "XFAIL:" $$xfail "$$lgn"; \
+ result_count $$1 "FAIL: " $$fail "$$red"; \
+ result_count $$1 "XPASS:" $$xpass "$$red"; \
+ result_count $$1 "ERROR:" $$error "$$mgn"; \
+ }; \
+ { \
+ echo "$(PACKAGE_STRING): $(subdir)/$(TEST_SUITE_LOG)" | \
+ $(am__rst_title); \
+ create_testsuite_report --no-color; \
+ echo; \
+ echo ".. contents:: :depth: 2"; \
+ echo; \
+ for b in $$bases; do echo $$b; done \
+ | $(am__create_global_log); \
+ } >$(TEST_SUITE_LOG).tmp || exit 1; \
+ mv $(TEST_SUITE_LOG).tmp $(TEST_SUITE_LOG); \
+ if $$success; then \
+ col="$$grn"; \
+ else \
+ col="$$red"; \
+ test x"$$VERBOSE" = x || cat $(TEST_SUITE_LOG); \
+ fi; \
+ echo "$${col}$$br$${std}"; \
+ echo "$${col}Testsuite summary"$(AM_TESTSUITE_SUMMARY_HEADER)"$${std}"; \
+ echo "$${col}$$br$${std}"; \
+ create_testsuite_report --maybe-color; \
+ echo "$$col$$br$$std"; \
+ if $$success; then :; else \
+ echo "$${col}See $(subdir)/$(TEST_SUITE_LOG)$${std}"; \
+ if test -n "$(PACKAGE_BUGREPORT)"; then \
+ echo "$${col}Please report to $(PACKAGE_BUGREPORT)$${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)
+ @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`; \
+ $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) TEST_LOGS="$$log_list"; \
+ exit $$?;
+recheck: all
+ @test -z "$(TEST_SUITE_LOG)" || 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; \
+ log_list=`for i in $$bases; do echo $$i.log; done`; \
+ log_list=`echo $$log_list`; \
+ $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) \
+ am__force_recheck=am--force-recheck \
+ TEST_LOGS="$$log_list"; \
+ exit $$?
+t_timer.log: t_timer$(EXEEXT)
+ @p='t_timer$(EXEEXT)'; \
+ b='t_timer'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+t_units.log: t_units$(EXEEXT)
+ @p='t_units$(EXEEXT)'; \
+ b='t_units'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+t_uuid.log: t_uuid$(EXEEXT)
+ @p='t_uuid$(EXEEXT)'; \
+ b='t_uuid'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+t_api.log: t_api$(EXEEXT)
+ @p='t_api$(EXEEXT)'; \
+ b='t_api'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+t_auth.log: t_auth$(EXEEXT)
+ @p='t_auth$(EXEEXT)'; \
+ b='t_auth'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+.test.log:
+ @p='$<'; \
+ $(am__set_b); \
+ $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+@am__EXEEXT_TRUE@.test$(EXEEXT).log:
+@am__EXEEXT_TRUE@ @p='$<'; \
+@am__EXEEXT_TRUE@ $(am__set_b); \
+@am__EXEEXT_TRUE@ $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \
+@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
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+ $(MAKE) $(AM_MAKEFLAGS) check-TESTS
+check: check-am
+all-am: Makefile $(PROGRAMS) $(LTLIBRARIES) $(MANS) $(HEADERS) \
+ iperf_config.h
+install-binPROGRAMS: install-libLTLIBRARIES
+
+installdirs:
+ for dir in "$(DESTDIR)$(bindir)" "$(DESTDIR)$(libdir)" "$(DESTDIR)$(man1dir)" "$(DESTDIR)$(man3dir)" "$(DESTDIR)$(includedir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "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)
+
+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)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+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)/dscp.Plo
+ -rm -f ./$(DEPDIR)/iperf3-main.Po
+ -rm -f ./$(DEPDIR)/iperf3_profile-cjson.Po
+ -rm -f ./$(DEPDIR)/iperf3_profile-dscp.Po
+ -rm -f ./$(DEPDIR)/iperf3_profile-iperf_api.Po
+ -rm -f ./$(DEPDIR)/iperf3_profile-iperf_auth.Po
+ -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_sctp.Po
+ -rm -f ./$(DEPDIR)/iperf3_profile-iperf_server_api.Po
+ -rm -f ./$(DEPDIR)/iperf3_profile-iperf_tcp.Po
+ -rm -f ./$(DEPDIR)/iperf3_profile-iperf_time.Po
+ -rm -f ./$(DEPDIR)/iperf3_profile-iperf_udp.Po
+ -rm -f ./$(DEPDIR)/iperf3_profile-iperf_util.Po
+ -rm -f ./$(DEPDIR)/iperf3_profile-main.Po
+ -rm -f ./$(DEPDIR)/iperf3_profile-net.Po
+ -rm -f ./$(DEPDIR)/iperf3_profile-tcp_info.Po
+ -rm -f ./$(DEPDIR)/iperf3_profile-timer.Po
+ -rm -f ./$(DEPDIR)/iperf3_profile-units.Po
+ -rm -f ./$(DEPDIR)/iperf_api.Plo
+ -rm -f ./$(DEPDIR)/iperf_auth.Plo
+ -rm -f ./$(DEPDIR)/iperf_client_api.Plo
+ -rm -f ./$(DEPDIR)/iperf_error.Plo
+ -rm -f ./$(DEPDIR)/iperf_locale.Plo
+ -rm -f ./$(DEPDIR)/iperf_sctp.Plo
+ -rm -f ./$(DEPDIR)/iperf_server_api.Plo
+ -rm -f ./$(DEPDIR)/iperf_tcp.Plo
+ -rm -f ./$(DEPDIR)/iperf_time.Plo
+ -rm -f ./$(DEPDIR)/iperf_udp.Plo
+ -rm -f ./$(DEPDIR)/iperf_util.Plo
+ -rm -f ./$(DEPDIR)/net.Plo
+ -rm -f ./$(DEPDIR)/t_api-t_api.Po
+ -rm -f ./$(DEPDIR)/t_auth-t_auth.Po
+ -rm -f ./$(DEPDIR)/t_timer-t_timer.Po
+ -rm -f ./$(DEPDIR)/t_units-t_units.Po
+ -rm -f ./$(DEPDIR)/t_uuid-t_uuid.Po
+ -rm -f ./$(DEPDIR)/tcp_info.Plo
+ -rm -f ./$(DEPDIR)/timer.Plo
+ -rm -f ./$(DEPDIR)/units.Plo
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-hdr distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am: install-includeHEADERS install-man
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-binPROGRAMS install-libLTLIBRARIES
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man: install-man1 install-man3
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/cjson.Plo
+ -rm -f ./$(DEPDIR)/dscp.Plo
+ -rm -f ./$(DEPDIR)/iperf3-main.Po
+ -rm -f ./$(DEPDIR)/iperf3_profile-cjson.Po
+ -rm -f ./$(DEPDIR)/iperf3_profile-dscp.Po
+ -rm -f ./$(DEPDIR)/iperf3_profile-iperf_api.Po
+ -rm -f ./$(DEPDIR)/iperf3_profile-iperf_auth.Po
+ -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_sctp.Po
+ -rm -f ./$(DEPDIR)/iperf3_profile-iperf_server_api.Po
+ -rm -f ./$(DEPDIR)/iperf3_profile-iperf_tcp.Po
+ -rm -f ./$(DEPDIR)/iperf3_profile-iperf_time.Po
+ -rm -f ./$(DEPDIR)/iperf3_profile-iperf_udp.Po
+ -rm -f ./$(DEPDIR)/iperf3_profile-iperf_util.Po
+ -rm -f ./$(DEPDIR)/iperf3_profile-main.Po
+ -rm -f ./$(DEPDIR)/iperf3_profile-net.Po
+ -rm -f ./$(DEPDIR)/iperf3_profile-tcp_info.Po
+ -rm -f ./$(DEPDIR)/iperf3_profile-timer.Po
+ -rm -f ./$(DEPDIR)/iperf3_profile-units.Po
+ -rm -f ./$(DEPDIR)/iperf_api.Plo
+ -rm -f ./$(DEPDIR)/iperf_auth.Plo
+ -rm -f ./$(DEPDIR)/iperf_client_api.Plo
+ -rm -f ./$(DEPDIR)/iperf_error.Plo
+ -rm -f ./$(DEPDIR)/iperf_locale.Plo
+ -rm -f ./$(DEPDIR)/iperf_sctp.Plo
+ -rm -f ./$(DEPDIR)/iperf_server_api.Plo
+ -rm -f ./$(DEPDIR)/iperf_tcp.Plo
+ -rm -f ./$(DEPDIR)/iperf_time.Plo
+ -rm -f ./$(DEPDIR)/iperf_udp.Plo
+ -rm -f ./$(DEPDIR)/iperf_util.Plo
+ -rm -f ./$(DEPDIR)/net.Plo
+ -rm -f ./$(DEPDIR)/t_api-t_api.Po
+ -rm -f ./$(DEPDIR)/t_auth-t_auth.Po
+ -rm -f ./$(DEPDIR)/t_timer-t_timer.Po
+ -rm -f ./$(DEPDIR)/t_units-t_units.Po
+ -rm -f ./$(DEPDIR)/t_uuid-t_uuid.Po
+ -rm -f ./$(DEPDIR)/tcp_info.Plo
+ -rm -f ./$(DEPDIR)/timer.Plo
+ -rm -f ./$(DEPDIR)/units.Plo
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-binPROGRAMS uninstall-includeHEADERS \
+ uninstall-libLTLIBRARIES uninstall-man
+
+uninstall-man: uninstall-man1 uninstall-man3
+
+.MAKE: all check-am install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-TESTS \
+ check-am clean clean-binPROGRAMS clean-generic \
+ clean-libLTLIBRARIES clean-libtool clean-noinstPROGRAMS \
+ cscopelist-am ctags ctags-am distclean distclean-compile \
+ distclean-generic distclean-hdr distclean-libtool \
+ 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-includeHEADERS install-info install-info-am \
+ install-libLTLIBRARIES install-man install-man1 install-man3 \
+ install-pdf install-pdf-am install-ps install-ps-am \
+ install-strip installcheck installcheck-am installdirs \
+ maintainer-clean maintainer-clean-generic mostlyclean \
+ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \
+ pdf pdf-am ps ps-am recheck tags tags-am uninstall \
+ uninstall-am uninstall-binPROGRAMS uninstall-includeHEADERS \
+ uninstall-libLTLIBRARIES uninstall-man uninstall-man1 \
+ uninstall-man3
+
+.PRECIOUS: Makefile
+
+
+# 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:
diff --git a/src/cjson.c b/src/cjson.c
new file mode 100644
index 0000000..02d8d60
--- /dev/null
+++ b/src/cjson.c
@@ -0,0 +1,3150 @@
+/*
+ Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ 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
+ AUTHORS 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 SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+*/
+
+/* cJSON */
+/* JSON parser in C. */
+
+/* disable warnings about old C89 functions in MSVC */
+#if !defined(_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER)
+#define _CRT_SECURE_NO_DEPRECATE
+#endif
+
+#ifdef __GNUC__
+#pragma GCC visibility push(default)
+#endif
+#if defined(_MSC_VER)
+#pragma warning (push)
+/* disable warning about single line comments in system headers */
+#pragma warning (disable : 4001)
+#endif
+
+#include "iperf_config.h"
+#include <string.h>
+#include <stdio.h>
+#include <math.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <ctype.h>
+#include <float.h>
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif
+#include <sys/types.h>
+
+#ifdef ENABLE_LOCALES
+#include <locale.h>
+#endif
+
+#if defined(_MSC_VER)
+#pragma warning (pop)
+#endif
+#ifdef __GNUC__
+#pragma GCC visibility pop
+#endif
+
+#include "cjson.h"
+
+/* define our own boolean type */
+#ifdef true
+#undef true
+#endif
+#define true ((cJSON_bool)1)
+
+#ifdef false
+#undef false
+#endif
+#define false ((cJSON_bool)0)
+
+/* define isnan and isinf for ANSI C, if in C99 or above, isnan and isinf has been defined in math.h */
+#ifndef isinf
+#define isinf(d) (isnan((d - d)) && !isnan(d))
+#endif
+#ifndef isnan
+#define isnan(d) (d != d)
+#endif
+
+#ifndef NAN
+#ifdef _WIN32
+#define NAN sqrt(-1.0)
+#else
+#define NAN 0.0/0.0
+#endif
+#endif
+
+#if defined(HAVE_INTTYPES_H)
+# include <inttypes.h>
+#else
+# ifndef PRIu64
+# if sizeof(long) == 8
+# define PRIu64 "lu"
+# else
+# define PRIu64 "llu"
+# endif
+# ifndef PRId64
+# if sizeof(long) == 8
+# define PRId64 "ld"
+# else
+# define PRId64 "lld"
+# endif
+# endif
+# endif
+#endif
+
+typedef struct {
+ const unsigned char *json;
+ size_t position;
+} error;
+static error global_error = { NULL, 0 };
+
+#ifndef LLONG_MAX
+#define LLONG_MAX 9223372036854775807LL
+#endif
+#ifndef LLONG_MIN
+#define LLONG_MIN (-LLONG_MAX - 1LL)
+#endif
+
+CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void)
+{
+ return (const char*) (global_error.json + global_error.position);
+}
+
+CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item)
+{
+ if (!cJSON_IsString(item))
+ {
+ return NULL;
+ }
+
+ return item->valuestring;
+}
+
+CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item)
+{
+ if (!cJSON_IsNumber(item))
+ {
+ return (double) NAN; // cppcheck-suppress invalidFunctionArg
+ }
+
+ return item->valuedouble;
+}
+
+/* This is a safeguard to prevent copy-pasters from using incompatible C and header files */
+#if (CJSON_VERSION_MAJOR != 1) || (CJSON_VERSION_MINOR != 7) || (CJSON_VERSION_PATCH != 15)
+ #error cJSON.h and cJSON.c have different versions. Make sure that both have the same.
+#endif
+
+CJSON_PUBLIC(const char*) cJSON_Version(void)
+{
+ static char version[15];
+ sprintf(version, "%i.%i.%i", CJSON_VERSION_MAJOR, CJSON_VERSION_MINOR, CJSON_VERSION_PATCH);
+
+ return version;
+}
+
+/* Case insensitive string comparison, doesn't consider two NULL pointers equal though */
+static int case_insensitive_strcmp(const unsigned char *string1, const unsigned char *string2)
+{
+ if ((string1 == NULL) || (string2 == NULL))
+ {
+ return 1;
+ }
+
+ if (string1 == string2)
+ {
+ return 0;
+ }
+
+ for(; tolower(*string1) == tolower(*string2); (void)string1++, string2++)
+ {
+ if (*string1 == '\0')
+ {
+ return 0;
+ }
+ }
+
+ return tolower(*string1) - tolower(*string2);
+}
+
+typedef struct internal_hooks
+{
+ void *(CJSON_CDECL *allocate)(size_t size);
+ void (CJSON_CDECL *deallocate)(void *pointer);
+ void *(CJSON_CDECL *reallocate)(void *pointer, size_t size);
+} internal_hooks;
+
+#if defined(_MSC_VER)
+/* work around MSVC error C2322: '...' address of dllimport '...' is not static */
+static void * CJSON_CDECL internal_malloc(size_t size)
+{
+ return malloc(size);
+}
+static void CJSON_CDECL internal_free(void *pointer)
+{
+ free(pointer);
+}
+static void * CJSON_CDECL internal_realloc(void *pointer, size_t size)
+{
+ return realloc(pointer, size);
+}
+#else
+#define internal_malloc malloc
+#define internal_free free
+#define internal_realloc realloc
+#endif
+
+/* strlen of character literals resolved at compile time */
+#define static_strlen(string_literal) (sizeof(string_literal) - sizeof(""))
+
+static internal_hooks global_hooks = { internal_malloc, internal_free, internal_realloc };
+
+static unsigned char* cJSON_strdup(const unsigned char* string, const internal_hooks * const hooks)
+{
+ size_t length = 0;
+ unsigned char *copy = NULL;
+
+ if (string == NULL)
+ {
+ return NULL;
+ }
+
+ length = strlen((const char*)string) + sizeof("");
+ copy = (unsigned char*)hooks->allocate(length);
+ if (copy == NULL)
+ {
+ return NULL;
+ }
+ memcpy(copy, string, length);
+
+ return copy;
+}
+
+CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks)
+{
+ if (hooks == NULL)
+ {
+ /* Reset hooks */
+ global_hooks.allocate = malloc;
+ global_hooks.deallocate = free;
+ global_hooks.reallocate = realloc;
+ return;
+ }
+
+ global_hooks.allocate = malloc;
+ if (hooks->malloc_fn != NULL)
+ {
+ global_hooks.allocate = hooks->malloc_fn;
+ }
+
+ global_hooks.deallocate = free;
+ if (hooks->free_fn != NULL)
+ {
+ global_hooks.deallocate = hooks->free_fn;
+ }
+
+ /* use realloc only if both free and malloc are used */
+ global_hooks.reallocate = NULL;
+ if ((global_hooks.allocate == malloc) && (global_hooks.deallocate == free))
+ {
+ global_hooks.reallocate = realloc;
+ }
+}
+
+/* Internal constructor. */
+static cJSON *cJSON_New_Item(const internal_hooks * const hooks)
+{
+ cJSON* node = (cJSON*)hooks->allocate(sizeof(cJSON));
+ if (node)
+ {
+ memset(node, '\0', sizeof(cJSON));
+ }
+
+ return node;
+}
+
+/* Delete a cJSON structure. */
+CJSON_PUBLIC(void) cJSON_Delete(cJSON *item)
+{
+ cJSON *next = NULL;
+ while (item != NULL)
+ {
+ next = item->next;
+ if (!(item->type & cJSON_IsReference) && (item->child != NULL))
+ {
+ cJSON_Delete(item->child);
+ }
+ if (!(item->type & cJSON_IsReference) && (item->valuestring != NULL))
+ {
+ global_hooks.deallocate(item->valuestring);
+ }
+ if (!(item->type & cJSON_StringIsConst) && (item->string != NULL))
+ {
+ global_hooks.deallocate(item->string);
+ }
+ global_hooks.deallocate(item);
+ item = next;
+ }
+}
+
+/* get the decimal point character of the current locale */
+static unsigned char get_decimal_point(void)
+{
+#ifdef ENABLE_LOCALES
+ struct lconv *lconv = localeconv();
+ return (unsigned char) lconv->decimal_point[0];
+#else
+ return '.';
+#endif
+}
+
+typedef struct
+{
+ const unsigned char *content;
+ size_t length;
+ size_t offset;
+ size_t depth; /* How deeply nested (in arrays/objects) is the input at the current offset. */
+ internal_hooks hooks;
+} parse_buffer;
+
+/* check if the given size is left to read in a given parse buffer (starting with 1) */
+#define can_read(buffer, size) ((buffer != NULL) && (((buffer)->offset + size) <= (buffer)->length))
+/* check if the buffer can be accessed at the given index (starting with 0) */
+#define can_access_at_index(buffer, index) ((buffer != NULL) && (((buffer)->offset + index) < (buffer)->length))
+#define cannot_access_at_index(buffer, index) (!can_access_at_index(buffer, index))
+/* get a pointer to the buffer at the position */
+#define buffer_at_offset(buffer) ((buffer)->content + (buffer)->offset)
+
+/* Parse the input text to generate a number, and populate the result into item. */
+static cJSON_bool parse_number(cJSON * const item, parse_buffer * const input_buffer)
+{
+ double number = 0;
+ unsigned char *after_end = NULL;
+ unsigned char number_c_string[64];
+ unsigned char decimal_point = get_decimal_point();
+ size_t i = 0;
+
+ if ((input_buffer == NULL) || (input_buffer->content == NULL))
+ {
+ return false;
+ }
+
+ /* copy the number into a temporary buffer and replace '.' with the decimal point
+ * of the current locale (for strtod)
+ * This also takes care of '\0' not necessarily being available for marking the end of the input */
+ for (i = 0; (i < (sizeof(number_c_string) - 1)) && can_access_at_index(input_buffer, i); i++)
+ {
+ switch (buffer_at_offset(input_buffer)[i])
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ case '+':
+ case '-':
+ case 'e':
+ case 'E':
+ number_c_string[i] = buffer_at_offset(input_buffer)[i];
+ break;
+
+ case '.':
+ number_c_string[i] = decimal_point;
+ break;
+
+ default:
+ goto loop_end;
+ }
+ }
+loop_end:
+ number_c_string[i] = '\0';
+
+ number = strtod((const char*)number_c_string, (char**)&after_end);
+ if (number_c_string == after_end)
+ {
+ return false; /* parse_error */
+ }
+
+ item->valuedouble = number;
+
+ /* use saturation in case of overflow */
+ if (number >= LLONG_MAX)
+ {
+ item->valueint = LLONG_MAX;
+ }
+ else if (number <= (double)LLONG_MIN)
+ {
+ item->valueint = LLONG_MIN;
+ }
+ else
+ {
+ item->valueint = (int64_t)number;
+ }
+
+ item->type = cJSON_Number;
+
+ input_buffer->offset += (size_t)(after_end - number_c_string);
+ return true;
+}
+
+/* don't ask me, but the original cJSON_SetNumberValue returns an integer or double */
+CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number)
+{
+ if (number >= LLONG_MAX)
+ {
+ object->valueint = LLONG_MAX;
+ }
+ else if (number <= (double)LLONG_MIN)
+ {
+ object->valueint = LLONG_MIN;
+ }
+ else
+ {
+ object->valueint = (int64_t)number;
+ }
+
+ return object->valuedouble = number;
+}
+
+CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring)
+{
+ char *copy = NULL;
+ /* if object's type is not cJSON_String or is cJSON_IsReference, it should not set valuestring */
+ if (!(object->type & cJSON_String) || (object->type & cJSON_IsReference))
+ {
+ return NULL;
+ }
+ if (strlen(valuestring) <= strlen(object->valuestring))
+ {
+ strcpy(object->valuestring, valuestring);
+ return object->valuestring;
+ }
+ copy = (char*) cJSON_strdup((const unsigned char*)valuestring, &global_hooks);
+ if (copy == NULL)
+ {
+ return NULL;
+ }
+ if (object->valuestring != NULL)
+ {
+ cJSON_free(object->valuestring);
+ }
+ object->valuestring = copy;
+
+ return copy;
+}
+
+typedef struct
+{
+ unsigned char *buffer;
+ size_t length;
+ size_t offset;
+ size_t depth; /* current nesting depth (for formatted printing) */
+ cJSON_bool noalloc;
+ cJSON_bool format; /* is this print a formatted print */
+ internal_hooks hooks;
+} printbuffer;
+
+/* realloc printbuffer if necessary to have at least "needed" bytes more */
+static unsigned char* ensure(printbuffer * const p, size_t needed)
+{
+ unsigned char *newbuffer = NULL;
+ size_t newsize = 0;
+
+ if ((p == NULL) || (p->buffer == NULL))
+ {
+ return NULL;
+ }
+
+ if ((p->length > 0) && (p->offset >= p->length))
+ {
+ /* make sure that offset is valid */
+ return NULL;
+ }
+
+ if (needed > SIZE_MAX)
+ {
+ /* sizes bigger than SIZE_MAX are currently not supported */
+ return NULL;
+ }
+
+ needed += p->offset + 1;
+ if (needed <= p->length)
+ {
+ return p->buffer + p->offset;
+ }
+
+ if (p->noalloc) {
+ return NULL;
+ }
+
+ /* calculate new buffer size */
+ if (needed > (SIZE_MAX / 2))
+ {
+ /* overflow of int, use SIZE_MAX if possible */
+ if (needed <= SIZE_MAX)
+ {
+ newsize = SIZE_MAX;
+ }
+ else
+ {
+ return NULL;
+ }
+ }
+ else
+ {
+ newsize = needed * 2;
+ }
+
+ if (p->hooks.reallocate != NULL)
+ {
+ /* reallocate with realloc if available */
+ newbuffer = (unsigned char*)p->hooks.reallocate(p->buffer, newsize);
+ if (newbuffer == NULL)
+ {
+ p->hooks.deallocate(p->buffer);
+ p->length = 0;
+ p->buffer = NULL;
+
+ return NULL;
+ }
+ }
+ else
+ {
+ /* otherwise reallocate manually */
+ newbuffer = (unsigned char*)p->hooks.allocate(newsize);
+ if (!newbuffer)
+ {
+ p->hooks.deallocate(p->buffer);
+ p->length = 0;
+ p->buffer = NULL;
+
+ return NULL;
+ }
+
+ memcpy(newbuffer, p->buffer, p->offset + 1);
+ p->hooks.deallocate(p->buffer);
+ }
+ p->length = newsize;
+ p->buffer = newbuffer;
+
+ return newbuffer + p->offset;
+}
+
+/* calculate the new length of the string in a printbuffer and update the offset */
+static void update_offset(printbuffer * const buffer)
+{
+ const unsigned char *buffer_pointer = NULL;
+ if ((buffer == NULL) || (buffer->buffer == NULL))
+ {
+ return;
+ }
+ buffer_pointer = buffer->buffer + buffer->offset;
+
+ buffer->offset += strlen((const char*)buffer_pointer);
+}
+
+/* securely comparison of floating-point variables */
+static cJSON_bool compare_double(double a, double b)
+{
+ double maxVal = fabs(a) > fabs(b) ? fabs(a) : fabs(b);
+ return (fabs(a - b) <= maxVal * DBL_EPSILON);
+}
+
+/* Render the number nicely from the given item into a string. */
+static cJSON_bool print_number(const cJSON * const item, printbuffer * const output_buffer)
+{
+ unsigned char *output_pointer = NULL;
+ double d = item->valuedouble;
+ int length = 0;
+ size_t i = 0;
+ unsigned char number_buffer[26] = {0}; /* temporary buffer to print the number into */
+ unsigned char decimal_point = get_decimal_point();
+ double test = 0.0;
+
+ if (output_buffer == NULL)
+ {
+ return false;
+ }
+
+ /* This checks for NaN and Infinity */
+ if (isnan(d) || isinf(d))
+ {
+ length = sprintf((char*)number_buffer, "null");
+ }
+ else if(d == (double)item->valueint)
+ {
+ length = sprintf((char*)number_buffer, "%" PRId64, item->valueint);
+ }
+ else
+ {
+ /* Try 15 decimal places of precision to avoid nonsignificant nonzero digits */
+ length = sprintf((char*)number_buffer, "%1.15g", d);
+
+ /* Check whether the original double can be recovered */
+ if ((sscanf((char*)number_buffer, "%lg", &test) != 1) || !compare_double((double)test, d))
+ {
+ /* If not, print with 17 decimal places of precision */
+ length = sprintf((char*)number_buffer, "%1.17g", d);
+ }
+ }
+
+ /* sprintf failed or buffer overrun occurred */
+ if ((length < 0) || (length > (int)(sizeof(number_buffer) - 1)))
+ {
+ return false;
+ }
+
+ /* reserve appropriate space in the output */
+ output_pointer = ensure(output_buffer, (size_t)length + sizeof(""));
+ if (output_pointer == NULL)
+ {
+ return false;
+ }
+
+ /* copy the printed number to the output and replace locale
+ * dependent decimal point with '.' */
+ for (i = 0; i < ((size_t)length); i++)
+ {
+ if (number_buffer[i] == decimal_point)
+ {
+ output_pointer[i] = '.';
+ continue;
+ }
+
+ output_pointer[i] = number_buffer[i];
+ }
+ output_pointer[i] = '\0';
+
+ output_buffer->offset += (size_t)length;
+
+ return true;
+}
+
+/* parse 4 digit hexadecimal number */
+static unsigned parse_hex4(const unsigned char * const input)
+{
+ unsigned int h = 0;
+ size_t i = 0;
+
+ for (i = 0; i < 4; i++)
+ {
+ /* parse digit */
+ if ((input[i] >= '0') && (input[i] <= '9'))
+ {
+ h += (unsigned int) input[i] - '0';
+ }
+ else if ((input[i] >= 'A') && (input[i] <= 'F'))
+ {
+ h += (unsigned int) 10 + input[i] - 'A';
+ }
+ else if ((input[i] >= 'a') && (input[i] <= 'f'))
+ {
+ h += (unsigned int) 10 + input[i] - 'a';
+ }
+ else /* invalid */
+ {
+ return 0;
+ }
+
+ if (i < 3)
+ {
+ /* shift left to make place for the next nibble */
+ h = h << 4;
+ }
+ }
+
+ return h;
+}
+
+/* converts a UTF-16 literal to UTF-8
+ * A literal can be one or two sequences of the form \uXXXX */
+static unsigned char utf16_literal_to_utf8(const unsigned char * const input_pointer, const unsigned char * const input_end, unsigned char **output_pointer)
+{
+ long unsigned int codepoint = 0;
+ unsigned int first_code = 0;
+ const unsigned char *first_sequence = input_pointer;
+ unsigned char utf8_length = 0;
+ unsigned char utf8_position = 0;
+ unsigned char sequence_length = 0;
+ unsigned char first_byte_mark = 0;
+
+ if ((input_end - first_sequence) < 6)
+ {
+ /* input ends unexpectedly */
+ goto fail;
+ }
+
+ /* get the first utf16 sequence */
+ first_code = parse_hex4(first_sequence + 2);
+
+ /* check that the code is valid */
+ if (((first_code >= 0xDC00) && (first_code <= 0xDFFF)))
+ {
+ goto fail;
+ }
+
+ /* UTF16 surrogate pair */
+ if ((first_code >= 0xD800) && (first_code <= 0xDBFF))
+ {
+ const unsigned char *second_sequence = first_sequence + 6;
+ unsigned int second_code = 0;
+ sequence_length = 12; /* \uXXXX\uXXXX */
+
+ if ((input_end - second_sequence) < 6)
+ {
+ /* input ends unexpectedly */
+ goto fail;
+ }
+
+ if ((second_sequence[0] != '\\') || (second_sequence[1] != 'u'))
+ {
+ /* missing second half of the surrogate pair */
+ goto fail;
+ }
+
+ /* get the second utf16 sequence */
+ second_code = parse_hex4(second_sequence + 2);
+ /* check that the code is valid */
+ if ((second_code < 0xDC00) || (second_code > 0xDFFF))
+ {
+ /* invalid second half of the surrogate pair */
+ goto fail;
+ }
+
+
+ /* calculate the unicode codepoint from the surrogate pair */
+ codepoint = 0x10000 + (((first_code & 0x3FF) << 10) | (second_code & 0x3FF));
+ }
+ else
+ {
+ sequence_length = 6; /* \uXXXX */
+ codepoint = first_code;
+ }
+
+ /* encode as UTF-8
+ * takes at maximum 4 bytes to encode:
+ * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */
+ if (codepoint < 0x80)
+ {
+ /* normal ascii, encoding 0xxxxxxx */
+ utf8_length = 1;
+ }
+ else if (codepoint < 0x800)
+ {
+ /* two bytes, encoding 110xxxxx 10xxxxxx */
+ utf8_length = 2;
+ first_byte_mark = 0xC0; /* 11000000 */
+ }
+ else if (codepoint < 0x10000)
+ {
+ /* three bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx */
+ utf8_length = 3;
+ first_byte_mark = 0xE0; /* 11100000 */
+ }
+ else if (codepoint <= 0x10FFFF)
+ {
+ /* four bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx 10xxxxxx */
+ utf8_length = 4;
+ first_byte_mark = 0xF0; /* 11110000 */
+ }
+ else
+ {
+ /* invalid unicode codepoint */
+ goto fail;
+ }
+
+ /* encode as utf8 */
+ for (utf8_position = (unsigned char)(utf8_length - 1); utf8_position > 0; utf8_position--)
+ {
+ /* 10xxxxxx */
+ (*output_pointer)[utf8_position] = (unsigned char)((codepoint | 0x80) & 0xBF);
+ codepoint >>= 6;
+ }
+ /* encode first byte */
+ if (utf8_length > 1)
+ {
+ (*output_pointer)[0] = (unsigned char)((codepoint | first_byte_mark) & 0xFF);
+ }
+ else
+ {
+ (*output_pointer)[0] = (unsigned char)(codepoint & 0x7F);
+ }
+
+ *output_pointer += utf8_length;
+
+ return sequence_length;
+
+fail:
+ return 0;
+}
+
+/* Parse the input text into an unescaped cinput, and populate item. */
+static cJSON_bool parse_string(cJSON * const item, parse_buffer * const input_buffer)
+{
+ const unsigned char *input_pointer = buffer_at_offset(input_buffer) + 1;
+ const unsigned char *input_end = buffer_at_offset(input_buffer) + 1;
+ unsigned char *output_pointer = NULL;
+ unsigned char *output = NULL;
+
+ /* not a string */
+ if (buffer_at_offset(input_buffer)[0] != '\"')
+ {
+ goto fail;
+ }
+
+ {
+ /* calculate approximate size of the output (overestimate) */
+ size_t allocation_length = 0;
+ size_t skipped_bytes = 0;
+ while (((size_t)(input_end - input_buffer->content) < input_buffer->length) && (*input_end != '\"'))
+ {
+ /* is escape sequence */
+ if (input_end[0] == '\\')
+ {
+ if ((size_t)(input_end + 1 - input_buffer->content) >= input_buffer->length)
+ {
+ /* prevent buffer overflow when last input character is a backslash */
+ goto fail;
+ }
+ skipped_bytes++;
+ input_end++;
+ }
+ input_end++;
+ }
+ if (((size_t)(input_end - input_buffer->content) >= input_buffer->length) || (*input_end != '\"'))
+ {
+ goto fail; /* string ended unexpectedly */
+ }
+
+ /* This is at most how much we need for the output */
+ allocation_length = (size_t) (input_end - buffer_at_offset(input_buffer)) - skipped_bytes;
+ output = (unsigned char*)input_buffer->hooks.allocate(allocation_length + sizeof(""));
+ if (output == NULL)
+ {
+ goto fail; /* allocation failure */
+ }
+ }
+
+ output_pointer = output;
+ /* loop through the string literal */
+ while (input_pointer < input_end)
+ {
+ if (*input_pointer != '\\')
+ {
+ *output_pointer++ = *input_pointer++;
+ }
+ /* escape sequence */
+ else
+ {
+ unsigned char sequence_length = 2;
+ if ((input_end - input_pointer) < 1)
+ {
+ goto fail;
+ }
+
+ switch (input_pointer[1])
+ {
+ case 'b':
+ *output_pointer++ = '\b';
+ break;
+ case 'f':
+ *output_pointer++ = '\f';
+ break;
+ case 'n':
+ *output_pointer++ = '\n';
+ break;
+ case 'r':
+ *output_pointer++ = '\r';
+ break;
+ case 't':
+ *output_pointer++ = '\t';
+ break;
+ case '\"':
+ case '\\':
+ case '/':
+ *output_pointer++ = input_pointer[1];
+ break;
+
+ /* UTF-16 literal */
+ case 'u':
+ sequence_length = utf16_literal_to_utf8(input_pointer, input_end, &output_pointer);
+ if (sequence_length == 0)
+ {
+ /* failed to convert UTF16-literal to UTF-8 */
+ goto fail;
+ }
+ break;
+
+ default:
+ goto fail;
+ }
+ input_pointer += sequence_length;
+ }
+ }
+
+ /* zero terminate the output */
+ *output_pointer = '\0';
+
+ item->type = cJSON_String;
+ item->valuestring = (char*)output;
+
+ input_buffer->offset = (size_t) (input_end - input_buffer->content);
+ input_buffer->offset++;
+
+ return true;
+
+fail:
+ if (output != NULL)
+ {
+ input_buffer->hooks.deallocate(output);
+ }
+
+ if (input_pointer != NULL)
+ {
+ input_buffer->offset = (size_t)(input_pointer - input_buffer->content);
+ }
+
+ return false;
+}
+
+/* Render the cstring provided to an escaped version that can be printed. */
+static cJSON_bool print_string_ptr(const unsigned char * const input, printbuffer * const output_buffer)
+{
+ const unsigned char *input_pointer = NULL;
+ unsigned char *output = NULL;
+ unsigned char *output_pointer = NULL;
+ size_t output_length = 0;
+ /* numbers of additional characters needed for escaping */
+ size_t escape_characters = 0;
+
+ if (output_buffer == NULL)
+ {
+ return false;
+ }
+
+ /* empty string */
+ if (input == NULL)
+ {
+ output = ensure(output_buffer, sizeof("\"\""));
+ if (output == NULL)
+ {
+ return false;
+ }
+ strcpy((char*)output, "\"\"");
+
+ return true;
+ }
+
+ /* set "flag" to 1 if something needs to be escaped */
+ for (input_pointer = input; *input_pointer; input_pointer++)
+ {
+ switch (*input_pointer)
+ {
+ case '\"':
+ case '\\':
+ case '\b':
+ case '\f':
+ case '\n':
+ case '\r':
+ case '\t':
+ /* one character escape sequence */
+ escape_characters++;
+ break;
+ default:
+ if (*input_pointer < 32)
+ {
+ /* UTF-16 escape sequence uXXXX */
+ escape_characters += 5;
+ }
+ break;
+ }
+ }
+ output_length = (size_t)(input_pointer - input) + escape_characters;
+
+ output = ensure(output_buffer, output_length + sizeof("\"\""));
+ if (output == NULL)
+ {
+ return false;
+ }
+
+ /* no characters have to be escaped */
+ if (escape_characters == 0)
+ {
+ output[0] = '\"';
+ memcpy(output + 1, input, output_length);
+ output[output_length + 1] = '\"';
+ output[output_length + 2] = '\0';
+
+ return true;
+ }
+
+ output[0] = '\"';
+ output_pointer = output + 1;
+ /* copy the string */
+ for (input_pointer = input; *input_pointer != '\0'; (void)input_pointer++, output_pointer++)
+ {
+ if ((*input_pointer > 31) && (*input_pointer != '\"') && (*input_pointer != '\\'))
+ {
+ /* normal character, copy */
+ *output_pointer = *input_pointer;
+ }
+ else
+ {
+ /* character needs to be escaped */
+ *output_pointer++ = '\\';
+ switch (*input_pointer)
+ {
+ case '\\':
+ *output_pointer = '\\';
+ break;
+ case '\"':
+ *output_pointer = '\"';
+ break;
+ case '\b':
+ *output_pointer = 'b';
+ break;
+ case '\f':
+ *output_pointer = 'f';
+ break;
+ case '\n':
+ *output_pointer = 'n';
+ break;
+ case '\r':
+ *output_pointer = 'r';
+ break;
+ case '\t':
+ *output_pointer = 't';
+ break;
+ default:
+ /* escape and print as unicode codepoint */
+ sprintf((char*)output_pointer, "u%04x", *input_pointer);
+ output_pointer += 4;
+ break;
+ }
+ }
+ }
+ output[output_length + 1] = '\"';
+ output[output_length + 2] = '\0';
+
+ return true;
+}
+
+/* Invoke print_string_ptr (which is useful) on an item. */
+static cJSON_bool print_string(const cJSON * const item, printbuffer * const p)
+{
+ return print_string_ptr((unsigned char*)item->valuestring, p);
+}
+
+/* Predeclare these prototypes. */
+static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer);
+static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer);
+static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer);
+static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer);
+static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer);
+static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer);
+
+/* Utility to jump whitespace and cr/lf */
+static parse_buffer *buffer_skip_whitespace(parse_buffer * const buffer)
+{
+ if ((buffer == NULL) || (buffer->content == NULL))
+ {
+ return NULL;
+ }
+
+ if (cannot_access_at_index(buffer, 0))
+ {
+ return buffer;
+ }
+
+ while (can_access_at_index(buffer, 0) && (buffer_at_offset(buffer)[0] <= 32))
+ {
+ buffer->offset++;
+ }
+
+ if (buffer->offset == buffer->length)
+ {
+ buffer->offset--;
+ }
+
+ return buffer;
+}
+
+/* skip the UTF-8 BOM (byte order mark) if it is at the beginning of a buffer */
+static parse_buffer *skip_utf8_bom(parse_buffer * const buffer)
+{
+ if ((buffer == NULL) || (buffer->content == NULL) || (buffer->offset != 0))
+ {
+ return NULL;
+ }
+
+ if (can_access_at_index(buffer, 4) && (strncmp((const char*)buffer_at_offset(buffer), "\xEF\xBB\xBF", 3) == 0))
+ {
+ buffer->offset += 3;
+ }
+
+ return buffer;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated)
+{
+ size_t buffer_length;
+
+ if (NULL == value)
+ {
+ return NULL;
+ }
+
+ /* Adding null character size due to require_null_terminated. */
+ buffer_length = strlen(value) + sizeof("");
+
+ return cJSON_ParseWithLengthOpts(value, buffer_length, return_parse_end, require_null_terminated);
+}
+
+/* Parse an object - create a new root, and populate. */
+CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated)
+{
+ parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } };
+ cJSON *item = NULL;
+
+ /* reset error position */
+ global_error.json = NULL;
+ global_error.position = 0;
+
+ if (value == NULL || 0 == buffer_length)
+ {
+ goto fail;
+ }
+
+ buffer.content = (const unsigned char*)value;
+ buffer.length = buffer_length;
+ buffer.offset = 0;
+ buffer.hooks = global_hooks;
+
+ item = cJSON_New_Item(&global_hooks);
+ if (item == NULL) /* memory fail */
+ {
+ goto fail;
+ }
+
+ if (!parse_value(item, buffer_skip_whitespace(skip_utf8_bom(&buffer))))
+ {
+ /* parse failure. ep is set. */
+ goto fail;
+ }
+
+ /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */
+ if (require_null_terminated)
+ {
+ buffer_skip_whitespace(&buffer);
+ if ((buffer.offset >= buffer.length) || buffer_at_offset(&buffer)[0] != '\0')
+ {
+ goto fail;
+ }
+ }
+ if (return_parse_end)
+ {
+ *return_parse_end = (const char*)buffer_at_offset(&buffer);
+ }
+
+ return item;
+
+fail:
+ if (item != NULL)
+ {
+ cJSON_Delete(item);
+ }
+
+ if (value != NULL)
+ {
+ error local_error;
+ local_error.json = (const unsigned char*)value;
+ local_error.position = 0;
+
+ if (buffer.offset < buffer.length)
+ {
+ local_error.position = buffer.offset;
+ }
+ else if (buffer.length > 0)
+ {
+ local_error.position = buffer.length - 1;
+ }
+
+ if (return_parse_end != NULL)
+ {
+ *return_parse_end = (const char*)local_error.json + local_error.position;
+ }
+
+ global_error = local_error;
+ }
+
+ return NULL;
+}
+
+/* Default options for cJSON_Parse */
+CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value)
+{
+ return cJSON_ParseWithOpts(value, 0, 0);
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length)
+{
+ return cJSON_ParseWithLengthOpts(value, buffer_length, 0, 0);
+}
+
+#define cjson_min(a, b) (((a) < (b)) ? (a) : (b))
+
+static unsigned char *print(const cJSON * const item, cJSON_bool format, const internal_hooks * const hooks)
+{
+ static const size_t default_buffer_size = 256;
+ printbuffer buffer[1];
+ unsigned char *printed = NULL;
+
+ memset(buffer, 0, sizeof(buffer));
+
+ /* create buffer */
+ buffer->buffer = (unsigned char*) hooks->allocate(default_buffer_size);
+ buffer->length = default_buffer_size;
+ buffer->format = format;
+ buffer->hooks = *hooks;
+ if (buffer->buffer == NULL)
+ {
+ goto fail;
+ }
+
+ /* print the value */
+ if (!print_value(item, buffer))
+ {
+ goto fail;
+ }
+ update_offset(buffer);
+
+ /* check if reallocate is available */
+ if (hooks->reallocate != NULL)
+ {
+ printed = (unsigned char*) hooks->reallocate(buffer->buffer, buffer->offset + 1);
+ if (printed == NULL) {
+ goto fail;
+ }
+ buffer->buffer = NULL;
+ }
+ else /* otherwise copy the JSON over to a new buffer */
+ {
+ printed = (unsigned char*) hooks->allocate(buffer->offset + 1);
+ if (printed == NULL)
+ {
+ goto fail;
+ }
+ memcpy(printed, buffer->buffer, cjson_min(buffer->length, buffer->offset + 1));
+ printed[buffer->offset] = '\0'; /* just to be sure */
+
+ /* free the buffer */
+ hooks->deallocate(buffer->buffer);
+ }
+
+ return printed;
+
+fail:
+ if (buffer->buffer != NULL)
+ {
+ hooks->deallocate(buffer->buffer);
+ }
+
+ if (printed != NULL)
+ {
+ hooks->deallocate(printed);
+ }
+
+ return NULL;
+}
+
+/* Render a cJSON item/entity/structure to text. */
+CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item)
+{
+ return (char*)print(item, true, &global_hooks);
+}
+
+CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item)
+{
+ return (char*)print(item, false, &global_hooks);
+}
+
+CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt)
+{
+ printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } };
+
+ if (prebuffer < 0)
+ {
+ return NULL;
+ }
+
+ p.buffer = (unsigned char*)global_hooks.allocate((size_t)prebuffer);
+ if (!p.buffer)
+ {
+ return NULL;
+ }
+
+ p.length = (size_t)prebuffer;
+ p.offset = 0;
+ p.noalloc = false;
+ p.format = fmt;
+ p.hooks = global_hooks;
+
+ if (!print_value(item, &p))
+ {
+ global_hooks.deallocate(p.buffer);
+ return NULL;
+ }
+
+ return (char*)p.buffer;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format)
+{
+ printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } };
+
+ if ((length < 0) || (buffer == NULL))
+ {
+ return false;
+ }
+
+ p.buffer = (unsigned char*)buffer;
+ p.length = (size_t)length;
+ p.offset = 0;
+ p.noalloc = true;
+ p.format = format;
+ p.hooks = global_hooks;
+
+ return print_value(item, &p);
+}
+
+/* Parser core - when encountering text, process appropriately. */
+static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer)
+{
+ if ((input_buffer == NULL) || (input_buffer->content == NULL))
+ {
+ return false; /* no input */
+ }
+
+ /* parse the different types of values */
+ /* null */
+ if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "null", 4) == 0))
+ {
+ item->type = cJSON_NULL;
+ input_buffer->offset += 4;
+ return true;
+ }
+ /* false */
+ if (can_read(input_buffer, 5) && (strncmp((const char*)buffer_at_offset(input_buffer), "false", 5) == 0))
+ {
+ item->type = cJSON_False;
+ input_buffer->offset += 5;
+ return true;
+ }
+ /* true */
+ if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "true", 4) == 0))
+ {
+ item->type = cJSON_True;
+ item->valueint = 1;
+ input_buffer->offset += 4;
+ return true;
+ }
+ /* string */
+ if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '\"'))
+ {
+ return parse_string(item, input_buffer);
+ }
+ /* number */
+ if (can_access_at_index(input_buffer, 0) && ((buffer_at_offset(input_buffer)[0] == '-') || ((buffer_at_offset(input_buffer)[0] >= '0') && (buffer_at_offset(input_buffer)[0] <= '9'))))
+ {
+ return parse_number(item, input_buffer);
+ }
+ /* array */
+ if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '['))
+ {
+ return parse_array(item, input_buffer);
+ }
+ /* object */
+ if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '{'))
+ {
+ return parse_object(item, input_buffer);
+ }
+
+ return false;
+}
+
+/* Render a value to text. */
+static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer)
+{
+ unsigned char *output = NULL;
+
+ if ((item == NULL) || (output_buffer == NULL))
+ {
+ return false;
+ }
+
+ switch ((item->type) & 0xFF)
+ {
+ case cJSON_NULL:
+ output = ensure(output_buffer, 5);
+ if (output == NULL)
+ {
+ return false;
+ }
+ strcpy((char*)output, "null");
+ return true;
+
+ case cJSON_False:
+ output = ensure(output_buffer, 6);
+ if (output == NULL)
+ {
+ return false;
+ }
+ strcpy((char*)output, "false");
+ return true;
+
+ case cJSON_True:
+ output = ensure(output_buffer, 5);
+ if (output == NULL)
+ {
+ return false;
+ }
+ strcpy((char*)output, "true");
+ return true;
+
+ case cJSON_Number:
+ return print_number(item, output_buffer);
+
+ case cJSON_Raw:
+ {
+ size_t raw_length = 0;
+ if (item->valuestring == NULL)
+ {
+ return false;
+ }
+
+ raw_length = strlen(item->valuestring) + sizeof("");
+ output = ensure(output_buffer, raw_length);
+ if (output == NULL)
+ {
+ return false;
+ }
+ memcpy(output, item->valuestring, raw_length);
+ return true;
+ }
+
+ case cJSON_String:
+ return print_string(item, output_buffer);
+
+ case cJSON_Array:
+ return print_array(item, output_buffer);
+
+ case cJSON_Object:
+ return print_object(item, output_buffer);
+
+ default:
+ return false;
+ }
+}
+
+/* Build an array from input text. */
+static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer)
+{
+ cJSON *head = NULL; /* head of the linked list */
+ cJSON *current_item = NULL;
+
+ if (input_buffer->depth >= CJSON_NESTING_LIMIT)
+ {
+ return false; /* to deeply nested */
+ }
+ input_buffer->depth++;
+
+ if (buffer_at_offset(input_buffer)[0] != '[')
+ {
+ /* not an array */
+ goto fail;
+ }
+
+ input_buffer->offset++;
+ buffer_skip_whitespace(input_buffer);
+ if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ']'))
+ {
+ /* empty array */
+ goto success;
+ }
+
+ /* check if we skipped to the end of the buffer */
+ if (cannot_access_at_index(input_buffer, 0))
+ {
+ input_buffer->offset--;
+ goto fail;
+ }
+
+ /* step back to character in front of the first element */
+ input_buffer->offset--;
+ /* loop through the comma separated array elements */
+ do
+ {
+ /* allocate next item */
+ cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks));
+ if (new_item == NULL)
+ {
+ goto fail; /* allocation failure */
+ }
+
+ /* attach next item to list */
+ if (head == NULL)
+ {
+ /* start the linked list */
+ current_item = head = new_item;
+ }
+ else
+ {
+ /* add to the end and advance */
+ current_item->next = new_item;
+ new_item->prev = current_item;
+ current_item = new_item;
+ }
+
+ /* parse next value */
+ input_buffer->offset++;
+ buffer_skip_whitespace(input_buffer);
+ if (!parse_value(current_item, input_buffer))
+ {
+ goto fail; /* failed to parse value */
+ }
+ buffer_skip_whitespace(input_buffer);
+ }
+ while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ','));
+
+ if (cannot_access_at_index(input_buffer, 0) || buffer_at_offset(input_buffer)[0] != ']')
+ {
+ goto fail; /* expected end of array */
+ }
+
+success:
+ input_buffer->depth--;
+
+ if (head != NULL) {
+ head->prev = current_item;
+ }
+
+ item->type = cJSON_Array;
+ item->child = head;
+
+ input_buffer->offset++;
+
+ return true;
+
+fail:
+ if (head != NULL)
+ {
+ cJSON_Delete(head);
+ }
+
+ return false;
+}
+
+/* Render an array to text */
+static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer)
+{
+ unsigned char *output_pointer = NULL;
+ size_t length = 0;
+ cJSON *current_element = item->child;
+
+ if (output_buffer == NULL)
+ {
+ return false;
+ }
+
+ /* Compose the output array. */
+ /* opening square bracket */
+ output_pointer = ensure(output_buffer, 1);
+ if (output_pointer == NULL)
+ {
+ return false;
+ }
+
+ *output_pointer = '[';
+ output_buffer->offset++;
+ output_buffer->depth++;
+
+ while (current_element != NULL)
+ {
+ if (!print_value(current_element, output_buffer))
+ {
+ return false;
+ }
+ update_offset(output_buffer);
+ if (current_element->next)
+ {
+ length = (size_t) (output_buffer->format ? 2 : 1);
+ output_pointer = ensure(output_buffer, length + 1);
+ if (output_pointer == NULL)
+ {
+ return false;
+ }
+ *output_pointer++ = ',';
+ if(output_buffer->format)
+ {
+ *output_pointer++ = ' ';
+ }
+ *output_pointer = '\0';
+ output_buffer->offset += length;
+ }
+ current_element = current_element->next;
+ }
+
+ output_pointer = ensure(output_buffer, 2);
+ if (output_pointer == NULL)
+ {
+ return false;
+ }
+ *output_pointer++ = ']';
+ *output_pointer = '\0';
+ output_buffer->depth--;
+
+ return true;
+}
+
+/* Build an object from the text. */
+static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer)
+{
+ cJSON *head = NULL; /* linked list head */
+ cJSON *current_item = NULL;
+
+ if (input_buffer->depth >= CJSON_NESTING_LIMIT)
+ {
+ return false; /* to deeply nested */
+ }
+ input_buffer->depth++;
+
+ if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '{'))
+ {
+ goto fail; /* not an object */
+ }
+
+ input_buffer->offset++;
+ buffer_skip_whitespace(input_buffer);
+ if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '}'))
+ {
+ goto success; /* empty object */
+ }
+
+ /* check if we skipped to the end of the buffer */
+ if (cannot_access_at_index(input_buffer, 0))
+ {
+ input_buffer->offset--;
+ goto fail;
+ }
+
+ /* step back to character in front of the first element */
+ input_buffer->offset--;
+ /* loop through the comma separated array elements */
+ do
+ {
+ /* allocate next item */
+ cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks));
+ if (new_item == NULL)
+ {
+ goto fail; /* allocation failure */
+ }
+
+ /* attach next item to list */
+ if (head == NULL)
+ {
+ /* start the linked list */
+ current_item = head = new_item;
+ }
+ else
+ {
+ /* add to the end and advance */
+ current_item->next = new_item;
+ new_item->prev = current_item;
+ current_item = new_item;
+ }
+
+ /* parse the name of the child */
+ input_buffer->offset++;
+ buffer_skip_whitespace(input_buffer);
+ if (!parse_string(current_item, input_buffer))
+ {
+ goto fail; /* failed to parse name */
+ }
+ buffer_skip_whitespace(input_buffer);
+
+ /* swap valuestring and string, because we parsed the name */
+ current_item->string = current_item->valuestring;
+ current_item->valuestring = NULL;
+
+ if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != ':'))
+ {
+ goto fail; /* invalid object */
+ }
+
+ /* parse the value */
+ input_buffer->offset++;
+ buffer_skip_whitespace(input_buffer);
+ if (!parse_value(current_item, input_buffer))
+ {
+ goto fail; /* failed to parse value */
+ }
+ buffer_skip_whitespace(input_buffer);
+ }
+ while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ','));
+
+ if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '}'))
+ {
+ goto fail; /* expected end of object */
+ }
+
+success:
+ input_buffer->depth--;
+
+ if (head != NULL) {
+ head->prev = current_item;
+ }
+
+ item->type = cJSON_Object;
+ item->child = head;
+
+ input_buffer->offset++;
+ return true;
+
+fail:
+ if (head != NULL)
+ {
+ cJSON_Delete(head);
+ }
+
+ return false;
+}
+
+/* Render an object to text. */
+static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer)
+{
+ unsigned char *output_pointer = NULL;
+ size_t length = 0;
+ cJSON *current_item = item->child;
+
+ if (output_buffer == NULL)
+ {
+ return false;
+ }
+
+ /* Compose the output: */
+ length = (size_t) (output_buffer->format ? 2 : 1); /* fmt: {\n */
+ output_pointer = ensure(output_buffer, length + 1);
+ if (output_pointer == NULL)
+ {
+ return false;
+ }
+
+ *output_pointer++ = '{';
+ output_buffer->depth++;
+ if (output_buffer->format)
+ {
+ *output_pointer++ = '\n';
+ }
+ output_buffer->offset += length;
+
+ while (current_item)
+ {
+ if (output_buffer->format)
+ {
+ size_t i;
+ output_pointer = ensure(output_buffer, output_buffer->depth);
+ if (output_pointer == NULL)
+ {
+ return false;
+ }
+ for (i = 0; i < output_buffer->depth; i++)
+ {
+ *output_pointer++ = '\t';
+ }
+ output_buffer->offset += output_buffer->depth;
+ }
+
+ /* print key */
+ if (!print_string_ptr((unsigned char*)current_item->string, output_buffer))
+ {
+ return false;
+ }
+ update_offset(output_buffer);
+
+ length = (size_t) (output_buffer->format ? 2 : 1);
+ output_pointer = ensure(output_buffer, length);
+ if (output_pointer == NULL)
+ {
+ return false;
+ }
+ *output_pointer++ = ':';
+ if (output_buffer->format)
+ {
+ *output_pointer++ = '\t';
+ }
+ output_buffer->offset += length;
+
+ /* print value */
+ if (!print_value(current_item, output_buffer))
+ {
+ return false;
+ }
+ update_offset(output_buffer);
+
+ /* print comma if not last */
+ length = ((size_t)(output_buffer->format ? 1 : 0) + (size_t)(current_item->next ? 1 : 0));
+ output_pointer = ensure(output_buffer, length + 1);
+ if (output_pointer == NULL)
+ {
+ return false;
+ }
+ if (current_item->next)
+ {
+ *output_pointer++ = ',';
+ }
+
+ if (output_buffer->format)
+ {
+ *output_pointer++ = '\n';
+ }
+ *output_pointer = '\0';
+ output_buffer->offset += length;
+
+ current_item = current_item->next;
+ }
+
+ output_pointer = ensure(output_buffer, output_buffer->format ? (output_buffer->depth + 1) : 2);
+ if (output_pointer == NULL)
+ {
+ return false;
+ }
+ if (output_buffer->format)
+ {
+ size_t i;
+ for (i = 0; i < (output_buffer->depth - 1); i++)
+ {
+ *output_pointer++ = '\t';
+ }
+ }
+ *output_pointer++ = '}';
+ *output_pointer = '\0';
+ output_buffer->depth--;
+
+ return true;
+}
+
+/* Get Array size/item / object item. */
+CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array)
+{
+ cJSON *child = NULL;
+ size_t size = 0;
+
+ if (array == NULL)
+ {
+ return 0;
+ }
+
+ child = array->child;
+
+ while(child != NULL)
+ {
+ size++;
+ child = child->next;
+ }
+
+ /* FIXME: Can overflow here. Cannot be fixed without breaking the API */
+
+ return (int)size;
+}
+
+static cJSON* get_array_item(const cJSON *array, size_t index)
+{
+ cJSON *current_child = NULL;
+
+ if (array == NULL)
+ {
+ return NULL;
+ }
+
+ current_child = array->child;
+ while ((current_child != NULL) && (index > 0))
+ {
+ index--;
+ current_child = current_child->next;
+ }
+
+ return current_child;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index)
+{
+ if (index < 0)
+ {
+ return NULL;
+ }
+
+ return get_array_item(array, (size_t)index);
+}
+
+static cJSON *get_object_item(const cJSON * const object, const char * const name, const cJSON_bool case_sensitive)
+{
+ cJSON *current_element = NULL;
+
+ if ((object == NULL) || (name == NULL))
+ {
+ return NULL;
+ }
+
+ current_element = object->child;
+ if (case_sensitive)
+ {
+ while ((current_element != NULL) && (current_element->string != NULL) && (strcmp(name, current_element->string) != 0))
+ {
+ current_element = current_element->next;
+ }
+ }
+ else
+ {
+ while ((current_element != NULL) && (case_insensitive_strcmp((const unsigned char*)name, (const unsigned char*)(current_element->string)) != 0))
+ {
+ current_element = current_element->next;
+ }
+ }
+
+ if ((current_element == NULL) || (current_element->string == NULL)) {
+ return NULL;
+ }
+
+ return current_element;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string)
+{
+ return get_object_item(object, string, false);
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string)
+{
+ return get_object_item(object, string, true);
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string)
+{
+ return cJSON_GetObjectItem(object, string) ? 1 : 0;
+}
+
+/* Utility for array list handling. */
+static void suffix_object(cJSON *prev, cJSON *item)
+{
+ prev->next = item;
+ item->prev = prev;
+}
+
+/* Utility for handling references. */
+static cJSON *create_reference(const cJSON *item, const internal_hooks * const hooks)
+{
+ cJSON *reference = NULL;
+ if (item == NULL)
+ {
+ return NULL;
+ }
+
+ reference = cJSON_New_Item(hooks);
+ if (reference == NULL)
+ {
+ return NULL;
+ }
+
+ memcpy(reference, item, sizeof(cJSON));
+ reference->string = NULL;
+ reference->type |= cJSON_IsReference;
+ reference->next = reference->prev = NULL;
+ return reference;
+}
+
+static cJSON_bool add_item_to_array(cJSON *array, cJSON *item)
+{
+ cJSON *child = NULL;
+
+ if ((item == NULL) || (array == NULL) || (array == item))
+ {
+ return false;
+ }
+
+ child = array->child;
+ /*
+ * To find the last item in array quickly, we use prev in array
+ */
+ if (child == NULL)
+ {
+ /* list is empty, start new one */
+ array->child = item;
+ item->prev = item;
+ item->next = NULL;
+ }
+ else
+ {
+ /* append to the end */
+ if (child->prev)
+ {
+ suffix_object(child->prev, item);
+ array->child->prev = item;
+ }
+ }
+
+ return true;
+}
+
+/* Add item to array/object. */
+CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item)
+{
+ return add_item_to_array(array, item);
+}
+
+#if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5))))
+ #pragma GCC diagnostic push
+#endif
+#ifdef __GNUC__
+#pragma GCC diagnostic ignored "-Wcast-qual"
+#endif
+/* helper function to cast away const */
+static void* cast_away_const(const void* string)
+{
+ return (void*)string;
+}
+#if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5))))
+ #pragma GCC diagnostic pop
+#endif
+
+
+static cJSON_bool add_item_to_object(cJSON * const object, const char * const string, cJSON * const item, const internal_hooks * const hooks, const cJSON_bool constant_key)
+{
+ char *new_key = NULL;
+ int new_type = cJSON_Invalid;
+
+ if ((object == NULL) || (string == NULL) || (item == NULL) || (object == item))
+ {
+ return false;
+ }
+
+ if (constant_key)
+ {
+ new_key = (char*)cast_away_const(string);
+ new_type = item->type | cJSON_StringIsConst;
+ }
+ else
+ {
+ new_key = (char*)cJSON_strdup((const unsigned char*)string, hooks);
+ if (new_key == NULL)
+ {
+ return false;
+ }
+
+ new_type = item->type & ~cJSON_StringIsConst;
+ }
+
+ if (!(item->type & cJSON_StringIsConst) && (item->string != NULL))
+ {
+ hooks->deallocate(item->string);
+ }
+
+ item->string = new_key;
+ item->type = new_type;
+
+ return add_item_to_array(object, item);
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item)
+{
+ return add_item_to_object(object, string, item, &global_hooks, false);
+}
+
+/* Add an item to an object with constant string as key */
+CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item)
+{
+ return add_item_to_object(object, string, item, &global_hooks, true);
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item)
+{
+ if (array == NULL)
+ {
+ return false;
+ }
+
+ return add_item_to_array(array, create_reference(item, &global_hooks));
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item)
+{
+ if ((object == NULL) || (string == NULL))
+ {
+ return false;
+ }
+
+ return add_item_to_object(object, string, create_reference(item, &global_hooks), &global_hooks, false);
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name)
+{
+ cJSON *null = cJSON_CreateNull();
+ if (add_item_to_object(object, name, null, &global_hooks, false))
+ {
+ return null;
+ }
+
+ cJSON_Delete(null);
+ return NULL;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name)
+{
+ cJSON *true_item = cJSON_CreateTrue();
+ if (add_item_to_object(object, name, true_item, &global_hooks, false))
+ {
+ return true_item;
+ }
+
+ cJSON_Delete(true_item);
+ return NULL;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name)
+{
+ cJSON *false_item = cJSON_CreateFalse();
+ if (add_item_to_object(object, name, false_item, &global_hooks, false))
+ {
+ return false_item;
+ }
+
+ cJSON_Delete(false_item);
+ return NULL;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean)
+{
+ cJSON *bool_item = cJSON_CreateBool(boolean);
+ if (add_item_to_object(object, name, bool_item, &global_hooks, false))
+ {
+ return bool_item;
+ }
+
+ cJSON_Delete(bool_item);
+ return NULL;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number)
+{
+ cJSON *number_item = cJSON_CreateNumber(number);
+ if (add_item_to_object(object, name, number_item, &global_hooks, false))
+ {
+ return number_item;
+ }
+
+ cJSON_Delete(number_item);
+ return NULL;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string)
+{
+ cJSON *string_item = cJSON_CreateString(string);
+ if (add_item_to_object(object, name, string_item, &global_hooks, false))
+ {
+ return string_item;
+ }
+
+ cJSON_Delete(string_item);
+ return NULL;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw)
+{
+ cJSON *raw_item = cJSON_CreateRaw(raw);
+ if (add_item_to_object(object, name, raw_item, &global_hooks, false))
+ {
+ return raw_item;
+ }
+
+ cJSON_Delete(raw_item);
+ return NULL;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name)
+{
+ cJSON *object_item = cJSON_CreateObject();
+ if (add_item_to_object(object, name, object_item, &global_hooks, false))
+ {
+ return object_item;
+ }
+
+ cJSON_Delete(object_item);
+ return NULL;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name)
+{
+ cJSON *array = cJSON_CreateArray();
+ if (add_item_to_object(object, name, array, &global_hooks, false))
+ {
+ return array;
+ }
+
+ cJSON_Delete(array);
+ return NULL;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item)
+{
+ if ((parent == NULL) || (item == NULL))
+ {
+ return NULL;
+ }
+
+ if (item != parent->child)
+ {
+ /* not the first element */
+ item->prev->next = item->next;
+ }
+ if (item->next != NULL)
+ {
+ /* not the last element */
+ item->next->prev = item->prev;
+ }
+
+ if (item == parent->child)
+ {
+ /* first element */
+ parent->child = item->next;
+ }
+ else if (item->next == NULL)
+ {
+ /* last element */
+ parent->child->prev = item->prev;
+ }
+
+ /* make sure the detached item doesn't point anywhere anymore */
+ item->prev = NULL;
+ item->next = NULL;
+
+ return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which)
+{
+ if (which < 0)
+ {
+ return NULL;
+ }
+
+ return cJSON_DetachItemViaPointer(array, get_array_item(array, (size_t)which));
+}
+
+CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which)
+{
+ cJSON_Delete(cJSON_DetachItemFromArray(array, which));
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string)
+{
+ cJSON *to_detach = cJSON_GetObjectItem(object, string);
+
+ return cJSON_DetachItemViaPointer(object, to_detach);
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string)
+{
+ cJSON *to_detach = cJSON_GetObjectItemCaseSensitive(object, string);
+
+ return cJSON_DetachItemViaPointer(object, to_detach);
+}
+
+CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string)
+{
+ cJSON_Delete(cJSON_DetachItemFromObject(object, string));
+}
+
+CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string)
+{
+ cJSON_Delete(cJSON_DetachItemFromObjectCaseSensitive(object, string));
+}
+
+/* Replace array/object items with new ones. */
+CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem)
+{
+ cJSON *after_inserted = NULL;
+
+ if (which < 0)
+ {
+ return false;
+ }
+
+ after_inserted = get_array_item(array, (size_t)which);
+ if (after_inserted == NULL)
+ {
+ return add_item_to_array(array, newitem);
+ }
+
+ newitem->next = after_inserted;
+ newitem->prev = after_inserted->prev;
+ after_inserted->prev = newitem;
+ if (after_inserted == array->child)
+ {
+ array->child = newitem;
+ }
+ else
+ {
+ newitem->prev->next = newitem;
+ }
+ return true;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement)
+{
+ if ((parent == NULL) || (replacement == NULL) || (item == NULL))
+ {
+ return false;
+ }
+
+ if (replacement == item)
+ {
+ return true;
+ }
+
+ replacement->next = item->next;
+ replacement->prev = item->prev;
+
+ if (replacement->next != NULL)
+ {
+ replacement->next->prev = replacement;
+ }
+ if (parent->child == item)
+ {
+ if (parent->child->prev == parent->child)
+ {
+ replacement->prev = replacement;
+ }
+ parent->child = replacement;
+ }
+ else
+ { /*
+ * To find the last item in array quickly, we use prev in array.
+ * We can't modify the last item's next pointer where this item was the parent's child
+ */
+ if (replacement->prev != NULL)
+ {
+ replacement->prev->next = replacement;
+ }
+ if (replacement->next == NULL)
+ {
+ parent->child->prev = replacement;
+ }
+ }
+
+ item->next = NULL;
+ item->prev = NULL;
+ cJSON_Delete(item);
+
+ return true;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem)
+{
+ if (which < 0)
+ {
+ return false;
+ }
+
+ return cJSON_ReplaceItemViaPointer(array, get_array_item(array, (size_t)which), newitem);
+}
+
+static cJSON_bool replace_item_in_object(cJSON *object, const char *string, cJSON *replacement, cJSON_bool case_sensitive)
+{
+ if ((replacement == NULL) || (string == NULL))
+ {
+ return false;
+ }
+
+ /* replace the name in the replacement */
+ if (!(replacement->type & cJSON_StringIsConst) && (replacement->string != NULL))
+ {
+ cJSON_free(replacement->string);
+ }
+ replacement->string = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks);
+ if (replacement->string == NULL)
+ {
+ return false;
+ }
+
+ replacement->type &= ~cJSON_StringIsConst;
+
+ return cJSON_ReplaceItemViaPointer(object, get_object_item(object, string, case_sensitive), replacement);
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem)
+{
+ return replace_item_in_object(object, string, newitem, false);
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object, const char *string, cJSON *newitem)
+{
+ return replace_item_in_object(object, string, newitem, true);
+}
+
+/* Create basic types: */
+CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void)
+{
+ cJSON *item = cJSON_New_Item(&global_hooks);
+ if(item)
+ {
+ item->type = cJSON_NULL;
+ }
+
+ return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void)
+{
+ cJSON *item = cJSON_New_Item(&global_hooks);
+ if(item)
+ {
+ item->type = cJSON_True;
+ }
+
+ return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void)
+{
+ cJSON *item = cJSON_New_Item(&global_hooks);
+ if(item)
+ {
+ item->type = cJSON_False;
+ }
+
+ return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean)
+{
+ cJSON *item = cJSON_New_Item(&global_hooks);
+ if(item)
+ {
+ item->type = boolean ? cJSON_True : cJSON_False;
+ }
+
+ return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num)
+{
+ cJSON *item = cJSON_New_Item(&global_hooks);
+ if(item)
+ {
+ item->type = cJSON_Number;
+ item->valuedouble = num;
+
+ /* use saturation in case of overflow */
+ if (num >= LLONG_MAX)
+ {
+ item->valueint = LLONG_MAX;
+ }
+ else if (num <= (double)LLONG_MIN)
+ {
+ item->valueint = LLONG_MIN;
+ }
+ else
+ {
+ item->valueint = (int64_t)num;
+ }
+ }
+
+ return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string)
+{
+ cJSON *item = cJSON_New_Item(&global_hooks);
+ if(item)
+ {
+ item->type = cJSON_String;
+ item->valuestring = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks);
+ if(!item->valuestring)
+ {
+ cJSON_Delete(item);
+ return NULL;
+ }
+ }
+
+ return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string)
+{
+ cJSON *item = cJSON_New_Item(&global_hooks);
+ if (item != NULL)
+ {
+ item->type = cJSON_String | cJSON_IsReference;
+ item->valuestring = (char*)cast_away_const(string);
+ }
+
+ return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child)
+{
+ cJSON *item = cJSON_New_Item(&global_hooks);
+ if (item != NULL) {
+ item->type = cJSON_Object | cJSON_IsReference;
+ item->child = (cJSON*)cast_away_const(child);
+ }
+
+ return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child) {
+ cJSON *item = cJSON_New_Item(&global_hooks);
+ if (item != NULL) {
+ item->type = cJSON_Array | cJSON_IsReference;
+ item->child = (cJSON*)cast_away_const(child);
+ }
+
+ return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw)
+{
+ cJSON *item = cJSON_New_Item(&global_hooks);
+ if(item)
+ {
+ item->type = cJSON_Raw;
+ item->valuestring = (char*)cJSON_strdup((const unsigned char*)raw, &global_hooks);
+ if(!item->valuestring)
+ {
+ cJSON_Delete(item);
+ return NULL;
+ }
+ }
+
+ return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void)
+{
+ cJSON *item = cJSON_New_Item(&global_hooks);
+ if(item)
+ {
+ item->type=cJSON_Array;
+ }
+
+ return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void)
+{
+ cJSON *item = cJSON_New_Item(&global_hooks);
+ if (item)
+ {
+ item->type = cJSON_Object;
+ }
+
+ return item;
+}
+
+/* Create Arrays: */
+CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count)
+{
+ size_t i = 0;
+ cJSON *n = NULL;
+ cJSON *p = NULL;
+ cJSON *a = NULL;
+
+ if ((count < 0) || (numbers == NULL))
+ {
+ return NULL;
+ }
+
+ a = cJSON_CreateArray();
+
+ for(i = 0; a && (i < (size_t)count); i++)
+ {
+ n = cJSON_CreateNumber(numbers[i]);
+ if (!n)
+ {
+ cJSON_Delete(a);
+ return NULL;
+ }
+ if(!i)
+ {
+ a->child = n;
+ }
+ else
+ {
+ suffix_object(p, n);
+ }
+ p = n;
+ }
+
+ if (a && a->child) {
+ a->child->prev = n;
+ }
+
+ return a;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count)
+{
+ size_t i = 0;
+ cJSON *n = NULL;
+ cJSON *p = NULL;
+ cJSON *a = NULL;
+
+ if ((count < 0) || (numbers == NULL))
+ {
+ return NULL;
+ }
+
+ a = cJSON_CreateArray();
+
+ for(i = 0; a && (i < (size_t)count); i++)
+ {
+ n = cJSON_CreateNumber((double)numbers[i]);
+ if(!n)
+ {
+ cJSON_Delete(a);
+ return NULL;
+ }
+ if(!i)
+ {
+ a->child = n;
+ }
+ else
+ {
+ suffix_object(p, n);
+ }
+ p = n;
+ }
+
+ if (a && a->child) {
+ a->child->prev = n;
+ }
+
+ return a;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count)
+{
+ size_t i = 0;
+ cJSON *n = NULL;
+ cJSON *p = NULL;
+ cJSON *a = NULL;
+
+ if ((count < 0) || (numbers == NULL))
+ {
+ return NULL;
+ }
+
+ a = cJSON_CreateArray();
+
+ for(i = 0; a && (i < (size_t)count); i++)
+ {
+ n = cJSON_CreateNumber(numbers[i]);
+ if(!n)
+ {
+ cJSON_Delete(a);
+ return NULL;
+ }
+ if(!i)
+ {
+ a->child = n;
+ }
+ else
+ {
+ suffix_object(p, n);
+ }
+ p = n;
+ }
+
+ if (a && a->child) {
+ a->child->prev = n;
+ }
+
+ return a;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count)
+{
+ size_t i = 0;
+ cJSON *n = NULL;
+ cJSON *p = NULL;
+ cJSON *a = NULL;
+
+ if ((count < 0) || (strings == NULL))
+ {
+ return NULL;
+ }
+
+ a = cJSON_CreateArray();
+
+ for (i = 0; a && (i < (size_t)count); i++)
+ {
+ n = cJSON_CreateString(strings[i]);
+ if(!n)
+ {
+ cJSON_Delete(a);
+ return NULL;
+ }
+ if(!i)
+ {
+ a->child = n;
+ }
+ else
+ {
+ suffix_object(p,n);
+ }
+ p = n;
+ }
+
+ if (a && a->child) {
+ a->child->prev = n;
+ }
+
+ return a;
+}
+
+/* Duplication */
+CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse)
+{
+ cJSON *newitem = NULL;
+ cJSON *child = NULL;
+ cJSON *next = NULL;
+ cJSON *newchild = NULL;
+
+ /* Bail on bad ptr */
+ if (!item)
+ {
+ goto fail;
+ }
+ /* Create new item */
+ newitem = cJSON_New_Item(&global_hooks);
+ if (!newitem)
+ {
+ goto fail;
+ }
+ /* Copy over all vars */
+ newitem->type = item->type & (~cJSON_IsReference);
+ newitem->valueint = item->valueint;
+ newitem->valuedouble = item->valuedouble;
+ if (item->valuestring)
+ {
+ newitem->valuestring = (char*)cJSON_strdup((unsigned char*)item->valuestring, &global_hooks);
+ if (!newitem->valuestring)
+ {
+ goto fail;
+ }
+ }
+ if (item->string)
+ {
+ newitem->string = (item->type&cJSON_StringIsConst) ? item->string : (char*)cJSON_strdup((unsigned char*)item->string, &global_hooks);
+ if (!newitem->string)
+ {
+ goto fail;
+ }
+ }
+ /* If non-recursive, then we're done! */
+ if (!recurse)
+ {
+ return newitem;
+ }
+ /* Walk the ->next chain for the child. */
+ child = item->child;
+ while (child != NULL)
+ {
+ newchild = cJSON_Duplicate(child, true); /* Duplicate (with recurse) each item in the ->next chain */
+ if (!newchild)
+ {
+ goto fail;
+ }
+ if (next != NULL)
+ {
+ /* If newitem->child already set, then crosswire ->prev and ->next and move on */
+ next->next = newchild;
+ newchild->prev = next;
+ next = newchild;
+ }
+ else
+ {
+ /* Set newitem->child and move to it */
+ newitem->child = newchild;
+ next = newchild;
+ }
+ child = child->next;
+ }
+ if (newitem && newitem->child)
+ {
+ newitem->child->prev = newchild;
+ }
+
+ return newitem;
+
+fail:
+ if (newitem != NULL)
+ {
+ cJSON_Delete(newitem);
+ }
+
+ return NULL;
+}
+
+static void skip_oneline_comment(char **input)
+{
+ *input += static_strlen("//");
+
+ for (; (*input)[0] != '\0'; ++(*input))
+ {
+ if ((*input)[0] == '\n') {
+ *input += static_strlen("\n");
+ return;
+ }
+ }
+}
+
+static void skip_multiline_comment(char **input)
+{
+ *input += static_strlen("/*");
+
+ for (; (*input)[0] != '\0'; ++(*input))
+ {
+ if (((*input)[0] == '*') && ((*input)[1] == '/'))
+ {
+ *input += static_strlen("*/");
+ return;
+ }
+ }
+}
+
+static void minify_string(char **input, char **output) {
+ (*output)[0] = (*input)[0];
+ *input += static_strlen("\"");
+ *output += static_strlen("\"");
+
+
+ for (; (*input)[0] != '\0'; (void)++(*input), ++(*output)) {
+ (*output)[0] = (*input)[0];
+
+ if ((*input)[0] == '\"') {
+ (*output)[0] = '\"';
+ *input += static_strlen("\"");
+ *output += static_strlen("\"");
+ return;
+ } else if (((*input)[0] == '\\') && ((*input)[1] == '\"')) {
+ (*output)[1] = (*input)[1];
+ *input += static_strlen("\"");
+ *output += static_strlen("\"");
+ }
+ }
+}
+
+CJSON_PUBLIC(void) cJSON_Minify(char *json)
+{
+ char *into = json;
+
+ if (json == NULL)
+ {
+ return;
+ }
+
+ while (json[0] != '\0')
+ {
+ switch (json[0])
+ {
+ case ' ':
+ case '\t':
+ case '\r':
+ case '\n':
+ json++;
+ break;
+
+ case '/':
+ if (json[1] == '/')
+ {
+ skip_oneline_comment(&json);
+ }
+ else if (json[1] == '*')
+ {
+ skip_multiline_comment(&json);
+ } else {
+ json++;
+ }
+ break;
+
+ case '\"':
+ minify_string(&json, (char**)&into);
+ break;
+
+ default:
+ into[0] = json[0];
+ json++;
+ into++;
+ }
+ }
+
+ /* and null-terminate. */
+ *into = '\0';
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item)
+{
+ if (item == NULL)
+ {
+ return false;
+ }
+
+ return (item->type & 0xFF) == cJSON_Invalid;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item)
+{
+ if (item == NULL)
+ {
+ return false;
+ }
+
+ return (item->type & 0xFF) == cJSON_False;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item)
+{
+ if (item == NULL)
+ {
+ return false;
+ }
+
+ return (item->type & 0xff) == cJSON_True;
+}
+
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item)
+{
+ if (item == NULL)
+ {
+ return false;
+ }
+
+ return (item->type & (cJSON_True | cJSON_False)) != 0;
+}
+CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item)
+{
+ if (item == NULL)
+ {
+ return false;
+ }
+
+ return (item->type & 0xFF) == cJSON_NULL;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item)
+{
+ if (item == NULL)
+ {
+ return false;
+ }
+
+ return (item->type & 0xFF) == cJSON_Number;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item)
+{
+ if (item == NULL)
+ {
+ return false;
+ }
+
+ return (item->type & 0xFF) == cJSON_String;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item)
+{
+ if (item == NULL)
+ {
+ return false;
+ }
+
+ return (item->type & 0xFF) == cJSON_Array;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item)
+{
+ if (item == NULL)
+ {
+ return false;
+ }
+
+ return (item->type & 0xFF) == cJSON_Object;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item)
+{
+ if (item == NULL)
+ {
+ return false;
+ }
+
+ return (item->type & 0xFF) == cJSON_Raw;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive)
+{
+ if ((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF)))
+ {
+ return false;
+ }
+
+ /* check if type is valid */
+ switch (a->type & 0xFF)
+ {
+ case cJSON_False:
+ case cJSON_True:
+ case cJSON_NULL:
+ case cJSON_Number:
+ case cJSON_String:
+ case cJSON_Raw:
+ case cJSON_Array:
+ case cJSON_Object:
+ break;
+
+ default:
+ return false;
+ }
+
+ /* identical objects are equal */
+ if (a == b)
+ {
+ return true;
+ }
+
+ switch (a->type & 0xFF)
+ {
+ /* in these cases and equal type is enough */
+ case cJSON_False:
+ case cJSON_True:
+ case cJSON_NULL:
+ return true;
+
+ case cJSON_Number:
+ if (compare_double(a->valuedouble, b->valuedouble))
+ {
+ return true;
+ }
+ return false;
+
+ case cJSON_String:
+ case cJSON_Raw:
+ if ((a->valuestring == NULL) || (b->valuestring == NULL))
+ {
+ return false;
+ }
+ if (strcmp(a->valuestring, b->valuestring) == 0)
+ {
+ return true;
+ }
+
+ return false;
+
+ case cJSON_Array:
+ {
+ cJSON *a_element = a->child;
+ cJSON *b_element = b->child;
+
+ for (; (a_element != NULL) && (b_element != NULL);)
+ {
+ if (!cJSON_Compare(a_element, b_element, case_sensitive))
+ {
+ return false;
+ }
+
+ a_element = a_element->next;
+ b_element = b_element->next;
+ }
+
+ /* one of the arrays is longer than the other */
+ if (a_element != b_element) {
+ return false;
+ }
+
+ return true;
+ }
+
+ case cJSON_Object:
+ {
+ cJSON *a_element = NULL;
+ cJSON *b_element = NULL;
+ cJSON_ArrayForEach(a_element, a)
+ {
+ /* TODO This has O(n^2) runtime, which is horrible! */
+ b_element = get_object_item(b, a_element->string, case_sensitive);
+ if (b_element == NULL)
+ {
+ return false;
+ }
+
+ if (!cJSON_Compare(a_element, b_element, case_sensitive))
+ {
+ return false;
+ }
+ }
+
+ /* doing this twice, once on a and b to prevent true comparison if a subset of b
+ * TODO: Do this the proper way, this is just a fix for now */
+ cJSON_ArrayForEach(b_element, b)
+ {
+ a_element = get_object_item(a, b_element->string, case_sensitive);
+ if (a_element == NULL)
+ {
+ return false;
+ }
+
+ if (!cJSON_Compare(b_element, a_element, case_sensitive))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ default:
+ return false;
+ }
+}
+
+CJSON_PUBLIC(void *) cJSON_malloc(size_t size)
+{
+ return global_hooks.allocate(size);
+}
+
+CJSON_PUBLIC(void) cJSON_free(void *object)
+{
+ global_hooks.deallocate(object);
+}
diff --git a/src/cjson.h b/src/cjson.h
new file mode 100644
index 0000000..52da40d
--- /dev/null
+++ b/src/cjson.h
@@ -0,0 +1,304 @@
+/*
+ Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ 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
+ AUTHORS 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 SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+*/
+
+#ifndef cJSON__h
+#define cJSON__h
+
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#if !defined(__WINDOWS__) && (defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32))
+#define __WINDOWS__
+#endif
+
+#ifdef __WINDOWS__
+
+/* When compiling for windows, we specify a specific calling convention to avoid issues where we are being called from a project with a different default calling convention. For windows you have 3 define options:
+
+CJSON_HIDE_SYMBOLS - Define this in the case where you don't want to ever dllexport symbols
+CJSON_EXPORT_SYMBOLS - Define this on library build when you want to dllexport symbols (default)
+CJSON_IMPORT_SYMBOLS - Define this if you want to dllimport symbol
+
+For *nix builds that support visibility attribute, you can define similar behavior by
+
+setting default visibility to hidden by adding
+-fvisibility=hidden (for gcc)
+or
+-xldscope=hidden (for sun cc)
+to CFLAGS
+
+then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJSON_EXPORT_SYMBOLS does
+
+*/
+
+#define CJSON_CDECL __cdecl
+#define CJSON_STDCALL __stdcall
+
+/* export symbols by default, this is necessary for copy pasting the C and header file */
+#if !defined(CJSON_HIDE_SYMBOLS) && !defined(CJSON_IMPORT_SYMBOLS) && !defined(CJSON_EXPORT_SYMBOLS)
+#define CJSON_EXPORT_SYMBOLS
+#endif
+
+#if defined(CJSON_HIDE_SYMBOLS)
+#define CJSON_PUBLIC(type) type CJSON_STDCALL
+#elif defined(CJSON_EXPORT_SYMBOLS)
+#define CJSON_PUBLIC(type) __declspec(dllexport) type CJSON_STDCALL
+#elif defined(CJSON_IMPORT_SYMBOLS)
+#define CJSON_PUBLIC(type) __declspec(dllimport) type CJSON_STDCALL
+#endif
+#else /* !__WINDOWS__ */
+#define CJSON_CDECL
+#define CJSON_STDCALL
+
+#if (defined(__GNUC__) || defined(__SUNPRO_CC) || defined (__SUNPRO_C)) && defined(CJSON_API_VISIBILITY)
+#define CJSON_PUBLIC(type) __attribute__((visibility("default"))) type
+#else
+#define CJSON_PUBLIC(type) type
+#endif
+#endif
+
+/* project version */
+#define CJSON_VERSION_MAJOR 1
+#define CJSON_VERSION_MINOR 7
+#define CJSON_VERSION_PATCH 15
+
+#include <stddef.h>
+
+/* cJSON Types: */
+#define cJSON_Invalid (0)
+#define cJSON_False (1 << 0)
+#define cJSON_True (1 << 1)
+#define cJSON_NULL (1 << 2)
+#define cJSON_Number (1 << 3)
+#define cJSON_String (1 << 4)
+#define cJSON_Array (1 << 5)
+#define cJSON_Object (1 << 6)
+#define cJSON_Raw (1 << 7) /* raw json */
+
+#define cJSON_IsReference 256
+#define cJSON_StringIsConst 512
+
+/* The cJSON structure: */
+typedef struct cJSON
+{
+ /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */
+ struct cJSON *next;
+ struct cJSON *prev;
+ /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */
+ struct cJSON *child;
+
+ /* The type of the item, as above. */
+ int type;
+
+ /* 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;
+ /* The item's number, if type==cJSON_Number */
+ double valuedouble;
+
+ /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */
+ char *string;
+} cJSON;
+
+typedef struct cJSON_Hooks
+{
+ /* malloc/free are CDECL on Windows regardless of the default calling convention of the compiler, so ensure the hooks allow passing those functions directly. */
+ void *(CJSON_CDECL *malloc_fn)(size_t sz);
+ void (CJSON_CDECL *free_fn)(void *ptr);
+} cJSON_Hooks;
+
+typedef int cJSON_bool;
+
+/* Limits how deeply nested arrays/objects can be before cJSON rejects to parse them.
+ * This is to prevent stack overflows. */
+#ifndef CJSON_NESTING_LIMIT
+#define CJSON_NESTING_LIMIT 1000
+#endif
+
+/* returns the version of cJSON as a string */
+CJSON_PUBLIC(const char*) cJSON_Version(void);
+
+/* Supply malloc, realloc and free functions to cJSON */
+CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks);
+
+/* Memory Management: the caller is always responsible to free the results from all variants of cJSON_Parse (with cJSON_Delete) and cJSON_Print (with stdlib free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The exception is cJSON_PrintPreallocated, where the caller has full responsibility of the buffer. */
+/* Supply a block of JSON, and this returns a cJSON object you can interrogate. */
+CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value);
+CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length);
+/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */
+/* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error so will match cJSON_GetErrorPtr(). */
+CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated);
+CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated);
+
+/* Render a cJSON entity to text for transfer/storage. */
+CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item);
+/* Render a cJSON entity to text for transfer/storage without any formatting. */
+CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item);
+/* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */
+CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt);
+/* Render a cJSON entity to text using a buffer already allocated in memory with given length. Returns 1 on success and 0 on failure. */
+/* NOTE: cJSON is not always 100% accurate in estimating how much memory it will use, so to be safe allocate 5 bytes more than you actually need */
+CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format);
+/* Delete a cJSON entity and all subentities. */
+CJSON_PUBLIC(void) cJSON_Delete(cJSON *item);
+
+/* Returns the number of items in an array (or object). */
+CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array);
+/* Retrieve item number "index" from array "array". Returns NULL if unsuccessful. */
+CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index);
+/* Get item "string" from object. Case insensitive. */
+CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string);
+CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string);
+CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string);
+/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */
+CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void);
+
+/* Check item type and return its value */
+CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item);
+CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item);
+
+/* These functions check the type of an item */
+CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item);
+
+/* These calls create a cJSON item of the appropriate type. */
+CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void);
+CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void);
+CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void);
+CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean);
+CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num);
+CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string);
+/* raw json */
+CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw);
+CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void);
+CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void);
+
+/* Create a string where valuestring references a string so
+ * it will not be freed by cJSON_Delete */
+CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string);
+/* Create an object/array that only references it's elements so
+ * they will not be freed by cJSON_Delete */
+CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child);
+CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child);
+
+/* These utilities create an Array of count items.
+ * The parameter count cannot be greater than the number of elements in the number array, otherwise array access will be out of bounds.*/
+CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count);
+CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count);
+CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count);
+CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count);
+
+/* Append item to the specified array/object. */
+CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item);
+CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item);
+/* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object.
+ * WARNING: When this function was used, make sure to always check that (item->type & cJSON_StringIsConst) is zero before
+ * writing to `item->string` */
+CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item);
+/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */
+CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item);
+CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item);
+
+/* Remove/Detach items from Arrays/Objects. */
+CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item);
+CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which);
+CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which);
+CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string);
+CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string);
+CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string);
+CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string);
+
+/* Update array items. */
+CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */
+CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement);
+CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem);
+CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem);
+CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object,const char *string,cJSON *newitem);
+
+/* Duplicate a cJSON item */
+CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse);
+/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will
+ * need to be released. With recurse!=0, it will duplicate any children connected to the item.
+ * The item->next and ->prev pointers are always zero on return from Duplicate. */
+/* Recursively compare two cJSON items for equality. If either a or b is NULL or invalid, they will be considered unequal.
+ * case_sensitive determines if object keys are treated case sensitive (1) or case insensitive (0) */
+CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive);
+
+/* Minify a strings, remove blank characters(such as ' ', '\t', '\r', '\n') from strings.
+ * The input pointer json cannot point to a read-only address area, such as a string constant,
+ * but should point to a readable and writable address area. */
+CJSON_PUBLIC(void) cJSON_Minify(char *json);
+
+/* Helper functions for creating and adding items to an object at the same time.
+ * They return the added item or NULL on failure. */
+CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name);
+CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name);
+CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name);
+CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean);
+CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number);
+CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string);
+CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw);
+CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name);
+CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name);
+
+/* When assigning an integer value, it needs to be propagated to valuedouble too. */
+#define cJSON_SetIntValue(object, number) ((object) ? (object)->valueint = (object)->valuedouble = (number) : (number))
+/* helper for the cJSON_SetNumberValue macro */
+CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number);
+#define cJSON_SetNumberValue(object, number) ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number))
+/* Change the valuestring of a cJSON_String object, only takes effect when type of object is cJSON_String */
+CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring);
+
+/* If the object is not a boolean type this does nothing and returns cJSON_Invalid else it returns the new type*/
+#define cJSON_SetBoolValue(object, boolValue) ( \
+ (object != NULL && ((object)->type & (cJSON_False|cJSON_True))) ? \
+ (object)->type=((object)->type &(~(cJSON_False|cJSON_True)))|((boolValue)?cJSON_True:cJSON_False) : \
+ cJSON_Invalid\
+)
+
+/* Macro for iterating over an array or object */
+#define cJSON_ArrayForEach(element, array) for(element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next)
+
+/* malloc/free objects using the malloc/free functions that have been set with cJSON_InitHooks */
+CJSON_PUBLIC(void *) cJSON_malloc(size_t size);
+CJSON_PUBLIC(void) cJSON_free(void *object);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/dscp.c b/src/dscp.c
new file mode 100644
index 0000000..14c282a
--- /dev/null
+++ b/src/dscp.c
@@ -0,0 +1,162 @@
+/* dscp lookup routines lifted wholesale from openssh */
+
+/*
+ * Copyright (c) 2000 Markus Friedl. All rights reserved.
+ * Copyright (c) 2005,2006 Damien Miller. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+#include <inttypes.h>
+
+#ifdef WIN32
+#define strcasecmp(a,b) _stricmp(a,b)
+#define snprintf _snprintf
+#endif
+
+int parse_qos(const char *cp);
+const char * iptos2str(int iptos);
+
+/*
+ * Definitions for IP type of service (ip_tos)
+ */
+
+#ifdef HAVE_NETINET_IN_SYSTM_H
+#include <netinet/in_systm.h>
+#endif
+#ifdef HAVE_NETINET_IP_H
+#include <netinet/ip.h>
+#endif
+
+#ifndef IPTOS_LOWDELAY
+# define IPTOS_LOWDELAY 0x10
+# define IPTOS_THROUGHPUT 0x08
+# define IPTOS_RELIABILITY 0x04
+# define IPTOS_LOWCOST 0x02
+# define IPTOS_MINCOST IPTOS_LOWCOST
+#endif /* IPTOS_LOWDELAY */
+
+/*
+ * Definitions for DiffServ Codepoints as per RFC2474
+ */
+#ifndef IPTOS_DSCP_AF11
+# define IPTOS_DSCP_AF11 0x28
+# define IPTOS_DSCP_AF12 0x30
+# define IPTOS_DSCP_AF13 0x38
+# define IPTOS_DSCP_AF21 0x48
+# define IPTOS_DSCP_AF22 0x50
+# define IPTOS_DSCP_AF23 0x58
+# define IPTOS_DSCP_AF31 0x68
+# define IPTOS_DSCP_AF32 0x70
+# define IPTOS_DSCP_AF33 0x78
+# define IPTOS_DSCP_AF41 0x88
+# define IPTOS_DSCP_AF42 0x90
+# define IPTOS_DSCP_AF43 0x98
+# define IPTOS_DSCP_EF 0xb8
+#endif /* IPTOS_DSCP_AF11 */
+
+#ifndef IPTOS_DSCP_CS0
+# define IPTOS_DSCP_CS0 0x00
+# define IPTOS_DSCP_CS1 0x20
+# define IPTOS_DSCP_CS2 0x40
+# define IPTOS_DSCP_CS3 0x60
+# define IPTOS_DSCP_CS4 0x80
+# define IPTOS_DSCP_CS5 0xa0
+# define IPTOS_DSCP_CS6 0xc0
+# define IPTOS_DSCP_CS7 0xe0
+#endif /* IPTOS_DSCP_CS0 */
+#ifndef IPTOS_DSCP_EF
+# define IPTOS_DSCP_EF 0xb8
+#endif /* IPTOS_DSCP_EF */
+#ifndef IPTOS_DSCP_VA
+# define IPTOS_DSCP_VA 0xb0
+#endif /* IPTOS_DSCP_VA */
+
+static const struct {
+ const char *name;
+ int value;
+} ipqos[] = {
+ { "af11", IPTOS_DSCP_AF11 },
+ { "af12", IPTOS_DSCP_AF12 },
+ { "af13", IPTOS_DSCP_AF13 },
+ { "af21", IPTOS_DSCP_AF21 },
+ { "af22", IPTOS_DSCP_AF22 },
+ { "af23", IPTOS_DSCP_AF23 },
+ { "af31", IPTOS_DSCP_AF31 },
+ { "af32", IPTOS_DSCP_AF32 },
+ { "af33", IPTOS_DSCP_AF33 },
+ { "af41", IPTOS_DSCP_AF41 },
+ { "af42", IPTOS_DSCP_AF42 },
+ { "af43", IPTOS_DSCP_AF43 },
+ { "cs0", IPTOS_DSCP_CS0 },
+ { "cs1", IPTOS_DSCP_CS1 },
+ { "cs2", IPTOS_DSCP_CS2 },
+ { "cs3", IPTOS_DSCP_CS3 },
+ { "cs4", IPTOS_DSCP_CS4 },
+ { "cs5", IPTOS_DSCP_CS5 },
+ { "cs6", IPTOS_DSCP_CS6 },
+ { "cs7", IPTOS_DSCP_CS7 },
+ { "ef", IPTOS_DSCP_EF },
+ { "va", IPTOS_DSCP_VA },
+ { "lowdelay", IPTOS_LOWDELAY },
+ { "throughput", IPTOS_THROUGHPUT },
+ { "reliability", IPTOS_RELIABILITY },
+ { NULL, -1 }
+};
+
+int
+parse_qos(const char *cp)
+{
+ unsigned int i;
+ char *ep = NULL;
+ long val;
+
+ if (cp == NULL)
+ return -1;
+ for (i = 0; ipqos[i].name != NULL; i++) {
+ if (strcasecmp(cp, ipqos[i].name) == 0)
+ return ipqos[i].value;
+ }
+ /* Try parsing as an integer */
+ /* Max DSCP value is 2**6 - 1 */
+ val = strtol(cp, &ep, 0);
+ if (*cp == '\0' || *ep != '\0' || val < 0 || val > 63)
+ return -1;
+ return val << 2;
+}
+
+const char *
+iptos2str(int iptos)
+{
+ int i;
+ static char iptos_str[sizeof "0xff"];
+ if (iptos < 0 || iptos > 64) iptos = 0;
+ for (i = 0; ipqos[i].name != NULL; i++) {
+ if (ipqos[i].value == iptos)
+ return ipqos[i].name;
+ }
+ snprintf(iptos_str, sizeof iptos_str, "0x%02x", iptos);
+ return iptos_str;
+}
diff --git a/src/flowlabel.h b/src/flowlabel.h
new file mode 100644
index 0000000..b245a38
--- /dev/null
+++ b/src/flowlabel.h
@@ -0,0 +1,78 @@
+/*
+ * iperf, Copyright (c) 2014, 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.
+ *
+ * If you have questions about your rights to use or distribute this
+ * software, please contact Berkeley Lab's Technology Transfer
+ * Department at TTD@lbl.gov.
+ *
+ * NOTICE. This software is owned by the U.S. Department of Energy.
+ * As such, the U.S. Government has been granted for itself and others
+ * acting on its behalf a paid-up, nonexclusive, irrevocable,
+ * worldwide license in the Software to reproduce, prepare derivative
+ * works, and perform publicly and display publicly. Beginning five
+ * (5) years after the date permission to assert copyright is obtained
+ * from the U.S. Department of Energy, and subject to any subsequent
+ * five (5) year renewals, the U.S. Government is granted for itself
+ * and others acting on its behalf a paid-up, nonexclusive,
+ * irrevocable, worldwide license in the Software to reproduce,
+ * prepare derivative works, distribute copies to the public, perform
+ * publicly and display publicly, and to permit others to do so.
+ *
+ * This code is distributed under a BSD style license, see the LICENSE
+ * file for complete information.
+ */
+#ifndef __FLOW_LABEL_H
+#define __FLOW_LABEL_H
+
+
+#include <linux/types.h>
+#include <linux/version.h>
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0)
+#include <linux/in6.h>
+#else
+#ifndef __ANDROID__
+/*
+ It is just a stripped copy of the Linux kernel header "linux/in6.h"
+ "Flow label" things are still not defined in "netinet/in*.h" headers,
+ but we cannot use "linux/in6.h" immediately because it currently
+ conflicts with "netinet/in.h" . (in kernel versions < 3.7.0)
+*/
+struct in6_flowlabel_req
+{
+ struct in6_addr flr_dst;
+ __u32 flr_label;
+ __u8 flr_action;
+ __u8 flr_share;
+ __u16 flr_flags;
+ __u16 flr_expires;
+ __u16 flr_linger;
+ __u32 __flr_pad;
+ /* Options in format of IPV6_PKTOPTIONS */
+};
+#endif
+
+#define IPV6_FL_A_GET 0
+#define IPV6_FL_A_PUT 1
+#define IPV6_FL_A_RENEW 2
+
+#define IPV6_FL_F_CREATE 1
+#define IPV6_FL_F_EXCL 2
+
+#define IPV6_FL_S_NONE 0
+#define IPV6_FL_S_EXCL 1
+#define IPV6_FL_S_PROCESS 2
+#define IPV6_FL_S_USER 3
+#define IPV6_FL_S_ANY 255
+
+#define IPV6_FLOWINFO_FLOWLABEL 0x000fffff
+#define IPV6_FLOWINFO_PRIORITY 0x0ff00000
+
+#endif
+#define IPV6_FLOWLABEL_MGR 32
+#define IPV6_FLOWINFO_SEND 33
+
+#endif
diff --git a/src/iperf.h b/src/iperf.h
new file mode 100644
index 0000000..7b270bd
--- /dev/null
+++ b/src/iperf.h
@@ -0,0 +1,463 @@
+/*
+ * 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.
+ *
+ * If you have questions about your rights to use or distribute this
+ * software, please contact Berkeley Lab's Technology Transfer
+ * Department at TTD@lbl.gov.
+ *
+ * NOTICE. This software is owned by the U.S. Department of Energy.
+ * As such, the U.S. Government has been granted for itself and others
+ * acting on its behalf a paid-up, nonexclusive, irrevocable,
+ * worldwide license in the Software to reproduce, prepare derivative
+ * works, and perform publicly and display publicly. Beginning five
+ * (5) years after the date permission to assert copyright is obtained
+ * from the U.S. Department of Energy, and subject to any subsequent
+ * five (5) year renewals, the U.S. Government is granted for itself
+ * and others acting on its behalf a paid-up, nonexclusive,
+ * irrevocable, worldwide license in the Software to reproduce,
+ * prepare derivative works, distribute copies to the public, perform
+ * publicly and display publicly, and to permit others to do so.
+ *
+ * This code is distributed under a BSD style license, see the LICENSE
+ * file for complete information.
+ */
+#ifndef __IPERF_H
+#define __IPERF_H
+
+#include "iperf_config.h"
+
+#include <sys/time.h>
+#include <sys/types.h>
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif
+#include <sys/select.h>
+#include <sys/socket.h>
+#ifndef _GNU_SOURCE
+# define _GNU_SOURCE
+#endif
+#ifdef HAVE_LINUX_TCP_H
+#include <linux/tcp.h>
+#else
+#include <netinet/tcp.h>
+#endif
+#include <net/if.h> // for IFNAMSIZ
+
+#if defined(HAVE_CPUSET_SETAFFINITY)
+#include <sys/param.h>
+#include <sys/cpuset.h>
+#endif /* HAVE_CPUSET_SETAFFINITY */
+
+#if defined(HAVE_INTTYPES_H)
+# include <inttypes.h>
+#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"
+
+#if defined(HAVE_SSL)
+#include <openssl/bio.h>
+#include <openssl/evp.h>
+#endif // HAVE_SSL
+
+#ifdef HAVE_PTHREAD
+#include <pthread.h>
+#endif // HAVE_PTHREAD
+
+/*
+ * Atomic types highly desired, but if not, we approximate what we need
+ * with normal integers and warn.
+ */
+#ifdef HAVE_STDATOMIC_H
+#include <stdatomic.h>
+#else
+#warning "No <stdatomic.h> available."
+typedef uint64_t atomic_uint_fast64_t;
+#endif // HAVE_STDATOMIC_H
+
+#if !defined(__IPERF_API_H)
+typedef uint_fast64_t iperf_size_t;
+typedef atomic_uint_fast64_t atomic_iperf_size_t;
+#endif // __IPERF_API_H
+
+struct iperf_interval_results
+{
+ 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 */
+ int64_t interval_packet_count;
+ int64_t interval_outoforder_packets;
+ int64_t interval_cnt_error;
+ int64_t packet_count;
+ double jitter;
+ 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 */
+#else
+ /* Just placeholders, never accessed. */
+ char *tcpInfo;
+#endif
+ long interval_retrans;
+ long snd_cwnd;
+ long snd_wnd;
+ TAILQ_ENTRY(iperf_interval_results) irlistentries;
+ void *custom_data;
+ long rtt;
+ long rttvar;
+ long pmtu;
+};
+
+struct iperf_stream_result
+{
+ 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_max_rtt;
+ long stream_min_rtt;
+ long stream_sum_rtt;
+ int stream_count_rtt;
+ long stream_max_snd_cwnd;
+ long stream_max_snd_wnd;
+ struct iperf_time start_time;
+ struct iperf_time end_time;
+ struct iperf_time start_time_fixed;
+ double sender_time;
+ double receiver_time;
+ TAILQ_HEAD(irlisthead, iperf_interval_results) interval_results;
+ void *data;
+};
+
+#define COOKIE_SIZE 37 /* size of an ascii uuid */
+struct iperf_settings
+{
+ int domain; /* AF_INET or AF_INET6 */
+ int socket_bufsize; /* window size for TCP */
+ int blksize; /* size of read/writes (-l) */
+ iperf_size_t rate; /* target data rate for application pacing*/
+ iperf_size_t bitrate_limit; /* server's maximum allowed total data rate for all streams*/
+ double bitrate_limit_interval; /* interval for averaging total data rate */
+ int bitrate_limit_stats_per_interval; /* calculated number of stats periods for averaging total data rate */
+ uint64_t fqrate; /* target data rate for FQ pacing*/
+ int pacing_timer; /* pacing timer in microseconds */
+ int burst; /* packets per burst */
+ int mss; /* for TCP MSS */
+ int ttl; /* IP TTL option */
+ int tos; /* type of service bit */
+ int flowlabel; /* IPv6 flow label */
+ iperf_size_t bytes; /* number of bytes to send */
+ iperf_size_t blocks; /* number of blocks (packets) to send */
+ char unit_format; /* -f */
+ int num_ostreams; /* SCTP initmsg settings */
+ int dont_fragment; /* Whether to set IP flag Do-Not_Fragment */
+#if defined(HAVE_SSL)
+ char *authtoken; /* Authentication token */
+ char *client_username;
+ char *client_password;
+ EVP_PKEY *client_rsa_pubkey;
+#endif // HAVE_SSL
+ 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 */
+};
+
+struct iperf_test;
+
+struct iperf_stream
+{
+ struct iperf_test* test;
+
+ pthread_t thr;
+ int done;
+
+ /* configurable members */
+ int local_port;
+ int remote_port;
+ int socket;
+ int id;
+ int sender;
+ /* XXX: is settings just a pointer to the same struct in iperf_test? if not,
+ should it be? */
+ struct iperf_settings *settings; /* pointer to structure settings */
+
+ /* non configurable members */
+ struct iperf_stream_result *result; /* structure pointer to result */
+ Timer *send_timer;
+ int green_light;
+ int buffer_fd; /* data to send, file descriptor */
+ char *buffer; /* data to send, mmapped */
+ int pending_size; /* pending data to send */
+ int diskfile_fd; /* file to send, file descriptor */
+ int diskfile_left; /* remaining file data on disk */
+
+ /*
+ * for udp measurements - This can be a structure outside stream, and
+ * stream can have a pointer to this
+ */
+ 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;
+ 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;
+ struct sockaddr_storage remote_addr;
+
+ int (*rcv) (struct iperf_stream * stream);
+ int (*snd) (struct iperf_stream * stream);
+
+ /* chained send/receive routines for -F mode */
+ int (*rcv2) (struct iperf_stream * stream);
+ int (*snd2) (struct iperf_stream * stream);
+
+// struct iperf_stream *next;
+ SLIST_ENTRY(iperf_stream) streams;
+
+ void *data;
+};
+
+struct protocol {
+ int id;
+ char *name;
+ int (*accept)(struct iperf_test *);
+ int (*listen)(struct iperf_test *);
+ int (*connect)(struct iperf_test *);
+ int (*send)(struct iperf_stream *);
+ int (*recv)(struct iperf_stream *);
+ int (*init)(struct iperf_test *);
+ SLIST_ENTRY(protocol) protocols;
+};
+
+struct iperf_textline {
+ char *line;
+ TAILQ_ENTRY(iperf_textline) textlineentries;
+};
+
+struct xbind_entry {
+ char *name;
+ struct addrinfo *ai;
+ TAILQ_ENTRY(xbind_entry) link;
+};
+
+enum iperf_mode {
+ SENDER = 1,
+ RECEIVER = 0,
+ BIDIRECTIONAL = -1
+};
+
+enum debug_level {
+ DEBUG_LEVEL_ERROR = 1,
+ DEBUG_LEVEL_WARN = 2,
+ DEBUG_LEVEL_INFO = 3,
+ DEBUG_LEVEL_DEBUG = 4,
+ DEBUG_LEVEL_MAX = 4
+};
+
+
+struct iperf_test
+{
+ pthread_mutex_t print_mutex;
+
+ char role; /* 'c' lient or 's' erver */
+ enum iperf_mode mode;
+ int sender_has_retransmits;
+ int other_side_has_retransmits; /* used if mode == BIDIRECTIONAL */
+ struct protocol *protocol;
+ signed char state;
+ char *server_hostname; /* -c option */
+ char *tmp_template;
+ char *bind_address; /* first -B option */
+ char *bind_dev; /* bind to network device */
+ TAILQ_HEAD(xbind_addrhead, xbind_entry) xbind_addrs; /* all -X opts */
+ int bind_port; /* --cport option */
+ int server_port;
+ int omit; /* duration of omit period (-O flag) */
+ int duration; /* total duration of test (-t flag) */
+ char *diskfile_name; /* -F option */
+ int affinity, server_affinity; /* -A option */
+#if defined(HAVE_CPUSET_SETAFFINITY)
+ cpuset_t cpumask;
+#endif /* HAVE_CPUSET_SETAFFINITY */
+ char *title; /* -T option */
+ char *extra_data; /* --extra-data */
+ char *congestion; /* -C option */
+ char *congestion_used; /* what was actually used */
+ char *remote_congestion_used; /* what the other side used */
+ char *pidfile; /* -P option */
+
+ char *logfile; /* --logfile option */
+ FILE *outfile;
+
+ int ctrl_sck;
+ int mapped_v4;
+ int listener;
+ int prot_listener;
+
+ int ctrl_sck_mss; /* MSS for the control channel */
+
+#if defined(HAVE_SSL)
+ char *server_authorized_users;
+ EVP_PKEY *server_rsa_private_key;
+ int server_skew_threshold;
+#endif // HAVE_SSL
+
+ /* boolean variables for Options */
+ int daemon; /* -D option */
+ int one_off; /* -1 option */
+ int no_delay; /* -N option */
+ int reverse; /* -R option */
+ int bidirectional; /* --bidirectional */
+ int verbose; /* -V option - verbose mode */
+ int json_output; /* -J option - JSON output */
+ 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 */
+ int get_server_output; /* --get-server-output */
+ int udp_counters_64bit; /* --use-64-bit-udp-counters */
+ int forceflush; /* --forceflush - flushing output at every interval */
+ int multisend;
+ int repeating_payload; /* --repeating-payload */
+ int timestamps; /* --timestamps */
+ char *timestamp_format;
+
+ char *json_output_string; /* rendered JSON output if json_output is set */
+ /* Select related parameters */
+ int max_fd;
+ fd_set read_set; /* set of read sockets */
+ fd_set write_set; /* set of write sockets */
+
+ /* Interval related members */
+ int omitting;
+ double stats_interval;
+ double reporter_interval;
+ void (*stats_callback) (struct iperf_test *);
+ void (*reporter_callback) (struct iperf_test *);
+ Timer *omit_timer;
+ Timer *timer;
+ int done;
+ Timer *stats_timer;
+ Timer *reporter_timer;
+
+ double cpu_util[3]; /* cpu utilization of the test - total, user, system */
+ double remote_cpu_util[3]; /* cpu utilization for the remote host/client - total, user, system */
+
+ int num_streams; /* total streams in the test (-P) */
+
+ atomic_iperf_size_t bytes_sent;
+ atomic_iperf_size_t blocks_sent;
+
+ 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 */
+ iperf_size_t bitrate_limit_last_interval_index; /* Index of the last interval traffic inserted into the cyclic array */
+ int bitrate_limit_exceeded; /* Set by callback routine when average data rate exceeded the server's bitrate limit */
+
+ int server_last_run_rc; /* Save last server run rc for next test */
+ uint server_forced_idle_restarts_count; /* count number of forced server restarts to make sure it is not stack */
+ uint server_forced_no_msg_restarts_count; /* count number of forced server restarts to make sure it is not stack */
+ uint server_test_number; /* count number of tests performed by a server */
+
+ char cookie[COOKIE_SIZE];
+// struct iperf_stream *streams; /* pointer to list of struct stream */
+ SLIST_HEAD(slisthead, iperf_stream) streams;
+ struct iperf_settings *settings;
+
+ SLIST_HEAD(plisthead, protocol) protocols;
+
+ /* callback functions */
+ void (*on_new_stream)(struct iperf_stream *);
+ void (*on_test_start)(struct iperf_test *);
+ void (*on_connect)(struct iperf_test *);
+ void (*on_test_finish)(struct iperf_test *);
+
+ /* cJSON handles for use when in -J mode */\
+ cJSON *json_top;
+ cJSON *json_start;
+ cJSON *json_connected;
+ cJSON *json_intervals;
+ cJSON *json_end;
+
+ /* Server output (use on client side only) */
+ char *server_output_text;
+ cJSON *json_server_output;
+
+ /* Server output (use on server side only) */
+ TAILQ_HEAD(iperf_textlisthead, iperf_textline) server_output_list;
+
+};
+
+/* default settings */
+#define PORT 5201 /* default port to listen on (don't use the same port as iperf2) */
+#define uS_TO_NS 1000
+#define mS_TO_US 1000
+#define SEC_TO_mS 1000
+#define SEC_TO_US 1000000LL
+#define UDP_RATE (1024 * 1024) /* 1 Mbps */
+#define OMIT 0 /* seconds */
+#define DURATION 10 /* seconds */
+
+#define SEC_TO_NS 1000000000LL /* too big for enum/const on some platforms */
+#define MAX_RESULT_STRING 4096
+
+#define UDP_BUFFER_EXTRA 1024
+
+/* constants for command line arg sanity checks */
+#define MB (1024 * 1024)
+#define MAX_TCP_BUFFER (512 * MB)
+#define MAX_BLOCKSIZE MB
+/* Minimum size UDP send is the size of two 32-bit ints followed by a 64-bit int */
+#define MIN_UDP_BLOCKSIZE (4 + 4 + 8)
+/* Maximum size UDP send is (64K - 1) - IP and UDP header sizes */
+#define MAX_UDP_BLOCKSIZE (65535 - 8 - 20)
+#define MIN_INTERVAL 0.1
+#define MAX_INTERVAL 60.0
+#define MAX_TIME 86400
+#define MAX_BURST 1000
+#define MAX_MSS (9 * 1024)
+#define MAX_STREAMS 128
+
+#define TIMESTAMP_FORMAT "%c "
+
+extern int gerror; /* error value from getaddrinfo(3), for use in internal error handling */
+
+/* UDP "connect" message and reply (textual value for Wireshark, etc. readability - legacy was numeric) */
+#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
+
+/* 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
+
+#endif /* !__IPERF_H */
diff --git a/src/iperf3.1 b/src/iperf3.1
new file mode 100644
index 0000000..344a762
--- /dev/null
+++ b/src/iperf3.1
@@ -0,0 +1,507 @@
+.TH IPERF3 1 "November 2023" ESnet "User Manuals"
+.SH NAME
+iperf3 \- perform network throughput tests
+.SH SYNOPSIS
+.B iperf3 -s [
+.I options
+.B ]
+.br
+.B iperf3 -c
+.I server
+.B [
+.I options
+.B ]
+
+.SH DESCRIPTION
+iperf3 is a tool for performing network throughput measurements.
+It can test TCP, UDP, or SCTP throughput.
+To perform an iperf3 test the user must establish both a server and a
+client.
+.PP
+The iperf3 executable contains both client and server functionality.
+An iperf3 server can be started using either of the -s or
+--server command-line parameters, for example:
+.IP
+\fCiperf3 -s\fR
+.IP
+\fCiperf3 --server \fR
+.PP
+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 available.
+.PP
+By default, the iperf3 server listens on TCP port 5201 for connections
+from an iperf3 client.
+A custom port can be specified by using the -p flag, for
+example:
+.IP
+\fCiperf3 -s -p 5002\fR
+.PP
+After the server is started, it will listen for connections from
+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:
+.IP
+\fCiperf3 -c iperf3.example.com\fR
+.IP
+\fCiperf3 -c 192.0.2.1\fR
+.IP
+\fCiperf3 -c 2001:db8::1\fR
+.PP
+If the iperf3 server is running on a non-default TCP port, that port
+number needs to be specified on the client as well:
+.IP
+\fCiperf3 -c iperf3.example.com -p 5002\fR
+.PP
+The initial TCP connection is used to exchange test parameters,
+control the start and end of the test, and to exchange test results.
+This is sometimes referred to as the "control connection".
+The actual test data is sent over a separate TCP connection, as a
+separate flow of UDP packets, or as an independent SCTP connection,
+depending on what protocol was specified by the client.
+.PP
+Normally, the test data is sent from the client to the server, and
+measures the upload speed of the client.
+Measuring the download speed from the server can be done by specifying
+the -R flag on the client.
+This causes data to be sent from the server to the client.
+.IP
+\fCiperf3 -c iperf3.example.com -p 5202 -R
+.PP
+Results are displayed on both the client and server.
+There will be at least one line of output per measurement interval (by
+default a measurement 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 shows the measurement interval data for
+the client.
+.PP
+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 \fC-R\fR flag will
+reverse these roles.
+.PP
+The client can be made to retrieve the server-side output for a given
+test by specifying the --get-server-output flag.
+.PP
+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
+after the test has finished, no JSON output will be emitted until the
+end of the test.
+.PP
+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 OPTIONS" section of the manual page
+below, as well as summarized in iperf3's help output, which can be
+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)
+.TP
+.BR -f ", " --format " "
+[kmgtKMGT] 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
+.TP
+.BR -I ", " --pidfile " \fIfile\fR"
+write a file with the process ID, 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
+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.
+.TP
+.BR -A ", " --affinity " \fIn/n,m\fR"
+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 \fIn\fR form of this argument (where \fIn\fR is a CPU number).
+In addition, on the client side you can override the server's
+affinity for just that one test, using the \fIn,m\fR 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).
+.TP
+.BR -B ", " --bind " \fIhost\fR[\fB%\fIdev\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.
+.TP
+.BR --bind-dev " \fIdev\fR"
+bind to the specified network interface.
+This option uses SO_BINDTODEVICE, and may require root permissions.
+(Available on Linux and possibly other systems.)
+.TP
+.BR -V ", " --verbose " "
+give more detailed output
+.TP
+.BR -J ", " --json " "
+output in JSON format
+.TP
+.BR --logfile " \fIfile\fR"
+send output to a log file.
+.TP
+.BR --forceflush " "
+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.
+By default, timestamps have the format emitted by
+.BR ctime ( 1 ).
+Optionally, \fC=\fR followed by
+a format specification can be passed to customize the
+timestamps, see
+.BR strftime ( 3 ).
+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
+will halt a test if no data is received from the sender for this
+number of ms (default to 12000 ms, or 2 minutes).
+.TP
+.BR --snd-timeout " \fI#\fR"
+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 -d ", " --debug " "
+emit debugging output.
+Primarily (perhaps exclusively) of use to developers.
+.TP
+.BR -v ", " --version " "
+show version information and quit
+.TP
+.BR -h ", " --help " "
+show a help synopsis
+
+.SH "SERVER SPECIFIC OPTIONS"
+.TP
+.BR -s ", " --server " "
+run in server mode
+.TP
+.BR -D ", " --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.
+.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
+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
+the client specifies a test of more than \fIn\fR bits per second, or
+if the average data sent or received by the client (including all data
+streams) is greater than \fIn\fR bits per second. The default limit
+is zero, which implies no limit. The interval over which to average
+the data rate is 5 seconds by default, but can be specified by adding
+a '/' and a number to the bitrate specifier.
+.TP
+.BR --rsa-private-key-path " \fIfile\fR"
+path to the RSA private key (not password-protected) used to decrypt
+authentication credentials from the client (if built with OpenSSL
+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.
+.SH "CLIENT SPECIFIC OPTIONS"
+.TP
+.BR -c ", " --client " \fIhost\fR[\fB%\fIdev\fR]"
+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.
+.TP
+.BR --sctp
+use SCTP rather than TCP (FreeBSD and Linux)
+.TP
+.BR -u ", " --udp
+use UDP rather than TCP
+.TP
+.BR --connect-timeout " \fIn\fR"
+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.
+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,
+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 the \--fq-rate flag.
+This option replaces the \--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,
+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 timer parameter smooth out the traffic
+emitted by iperf3, but potentially at the cost of performance due to
+more frequent timer processing.
+.TP
+.BR --fq-rate " \fIn\fR[KMGT]"
+Set a rate to be used with fair-queueing based socket-level pacing,
+in bits per second.
+This pacing (if specified) will be in addition to any pacing due to
+iperf3's internal throughput pacing (\-b/\--bitrate flag), and both can be
+specified for the same test.
+Only available on platforms supporting the
+\fCSO_MAX_PACING_RATE\fR socket option (currently only Linux).
+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.
+.TP
+.BR -t ", " --time " \fIn\fR"
+time in seconds to transmit for (default 10 secs)
+.TP
+.BR -n ", " --bytes " \fIn\fR[KMGT]"
+number of bytes to transmit (instead of \-t)
+.TP
+.BR -k ", " --blockcount " \fIn\fR[KMGT]"
+number of blocks (packets) to transmit (instead of \-t or \-n)
+.TP
+.BR -l ", " --length " \fIn\fR[KMGT]"
+length of buffer to read or write. For TCP tests, the default value
+is 128KB.
+In the case of UDP, iperf3 tries to dynamically determine a reasonable
+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)
+.TP
+.BR -P ", " --parallel " \fIn\fR"
+number of parallel client streams to run. iperf3 will spawn off a
+separate thread for each test stream. Using multiple streams may
+result in higher throughput than a single stream.
+.TP
+.BR -R ", " --reverse
+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
+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)).
+.TP
+.BR -M ", " --set-mss " \fIn\fR"
+set TCP/SCTP maximum segment size (MTU - 40 bytes)
+.TP
+.BR -N ", " --no-delay " "
+set TCP/SCTP no delay, disabling Nagle's Algorithm
+.TP
+.BR -4 ", " --version4 " "
+only use IPv4
+.TP
+.BR -6 ", " --version6 " "
+only use IPv6
+.TP
+.BR -S ", " --tos " \fIn\fR"
+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.
+.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).
+.TP
+.BR -L ", " --flowlabel " \fIn\fR"
+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).
+The \fB--B\fR 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 association. Specifying at least
+one \fB--X\fR name will disable this behaviour.
+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 \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
+which do not resolve to addresses within the
+specified protocol family will be ignored.
+.TP
+.BR --nstreams " \fIn\fR"
+Set number of SCTP streams.
+.TP
+.BR -Z ", " --zerocopy " "
+Use a "zero copy" method of sending data, such as sendfile(2),
+instead of the usual write(2).
+.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
+period.
+.TP
+.BR -T ", " --title " \fIstr\fR"
+Prefix every output line with this string.
+.TP
+.BR --extra-data " \fIstr\fR"
+Specify an extra data string field to be included in JSON output.
+.TP
+.BR -C ", " --congestion " \fIalgo\fR"
+Set the congestion control algorithm (Linux and FreeBSD only). An
+older
+.B --linux-congestion
+synonym for this flag is accepted but is deprecated.
+.TP
+.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.
+.TP
+.BR --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 behavior at some point in the future.
+.TP
+.BR --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.
+.TP
+.BR --dont-fragment
+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.
+.TP
+.BR --rsa-public-key-path " \fIfile\fR"
+path to the RSA public key used to encrypt authentication credentials
+(if built with OpenSSL support)
+
+.SH EXAMPLES
+.SS "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 additionally 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 keypair follows:
+.sp 1
+.in +.5i
+> openssl genrsa -des3 -out private.pem 2048
+.sp 0
+> openssl rsa -in private.pem -outform PEM -pubout -out public.pem
+.sp 0
+> openssl rsa -in private.pem -out private_not_protected.pem -outform PEM
+.in -.5i
+.sp 1
+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.
+.SS "Authentication - Authorized users configuration file"
+A simple plaintext file must be provided to the iperf3 server in order to specify
+the authorized user credentials.
+The file is a simple list of comma-separated pairs of a username and a
+corresponding password hash.
+The password hash is a SHA256 hash of the string "{$user}$password".
+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
+.sp 0
+> echo -n "{$S_USER}$S_PASSWD" | sha256sum | awk '{ print $1 }'
+.in -.5i
+.sp 1
+An example of a password file (with an entry corresponding to the
+above username and password) is given below:
+.sp 0
+.in +.5i
+> cat credentials.csv
+.sp 0
+# file format: username,sha256
+.sp 0
+mario,bf7a49a846d44b454a5d11e7acfaf13d138bbe0b7483aa3e050879700572709b
+.in -.5i
+.sp 1
+
+.SH AUTHORS
+A list of the contributors to iperf3 can be found within the
+documentation located at
+\fChttps://software.es.net/iperf/dev.html#authors\fR.
+
+.SH "SEE ALSO"
+libiperf(3),
+https://software.es.net/iperf
diff --git a/src/iperf_api.c b/src/iperf_api.c
new file mode 100644
index 0000000..eb23403
--- /dev/null
+++ b/src/iperf_api.c
@@ -0,0 +1,5037 @@
+/*
+ * iperf, Copyright (c) 2014-2022, 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.
+ *
+ * If you have questions about your rights to use or distribute this
+ * software, please contact Berkeley Lab's Technology Transfer
+ * Department at TTD@lbl.gov.
+ *
+ * NOTICE. This software is owned by the U.S. Department of Energy.
+ * As such, the U.S. Government has been granted for itself and others
+ * acting on its behalf a paid-up, nonexclusive, irrevocable,
+ * worldwide license in the Software to reproduce, prepare derivative
+ * works, and perform publicly and display publicly. Beginning five
+ * (5) years after the date permission to assert copyright is obtained
+ * from the U.S. Department of Energy, and subject to any subsequent
+ * five (5) year renewals, the U.S. Government is granted for itself
+ * and others acting on its behalf a paid-up, nonexclusive,
+ * irrevocable, worldwide license in the Software to reproduce,
+ * prepare derivative works, distribute copies to the public, perform
+ * publicly and display publicly, and to permit others to do so.
+ *
+ * This code is distributed under a BSD style license, see the LICENSE file
+ * for complete information.
+ */
+#ifndef _GNU_SOURCE
+# define _GNU_SOURCE
+#endif
+#define __USE_GNU
+
+#include "iperf_config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <getopt.h>
+#include <errno.h>
+#include <signal.h>
+#include <unistd.h>
+#include <assert.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sched.h>
+#include <setjmp.h>
+#include <stdarg.h>
+#include <math.h>
+
+#if defined(HAVE_CPUSET_SETAFFINITY)
+#include <sys/param.h>
+#include <sys/cpuset.h>
+#endif /* HAVE_CPUSET_SETAFFINITY */
+
+#if defined(__CYGWIN__) || defined(_WIN32) || defined(_WIN64) || defined(__WINDOWS__)
+#define CPU_SETSIZE __CPU_SETSIZE
+#endif /* __CYGWIN__, _WIN32, _WIN64, __WINDOWS__ */
+
+#if defined(HAVE_SETPROCESSAFFINITYMASK)
+#include <Windows.h>
+#endif /* HAVE_SETPROCESSAFFINITYMASK */
+
+#include "net.h"
+#include "iperf.h"
+#include "iperf_api.h"
+#include "iperf_udp.h"
+#include "iperf_tcp.h"
+#if defined(HAVE_SCTP_H)
+#include "iperf_sctp.h"
+#endif /* HAVE_SCTP_H */
+#include "timer.h"
+
+#include "cjson.h"
+#include "units.h"
+#include "iperf_util.h"
+#include "iperf_locale.h"
+#include "version.h"
+#if defined(HAVE_SSL)
+#include <openssl/bio.h>
+#include <openssl/err.h>
+#include "iperf_auth.h"
+#endif /* HAVE_SSL */
+
+/* Forwards. */
+static int send_parameters(struct iperf_test *test);
+static int get_parameters(struct iperf_test *test);
+static int send_results(struct iperf_test *test);
+static int get_results(struct iperf_test *test);
+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);
+
+
+/*************************** Print usage functions ****************************/
+
+void
+usage()
+{
+ fputs(usage_shortstr, stderr);
+}
+
+
+void
+usage_long(FILE *f)
+{
+ fprintf(f, usage_longstr, DEFAULT_NO_MSG_RCVD_TIMEOUT, UDP_RATE / (1024*1024), DEFAULT_PACING_TIMER, DURATION, DEFAULT_TCP_BLKSIZE / 1024, DEFAULT_UDP_BLKSIZE);
+}
+
+
+void warning(const char *str)
+{
+ fprintf(stderr, "warning: %s\n", str);
+}
+
+
+/************** Getter routines for some fields inside iperf_test *************/
+
+int
+iperf_get_verbose(struct iperf_test *ipt)
+{
+ return ipt->verbose;
+}
+
+int
+iperf_get_control_socket(struct iperf_test *ipt)
+{
+ return ipt->ctrl_sck;
+}
+
+int
+iperf_get_control_socket_mss(struct iperf_test *ipt)
+{
+ return ipt->ctrl_sck_mss;
+}
+
+int
+iperf_get_test_omit(struct iperf_test *ipt)
+{
+ return ipt->omit;
+}
+
+int
+iperf_get_test_duration(struct iperf_test *ipt)
+{
+ return ipt->duration;
+}
+
+uint64_t
+iperf_get_test_rate(struct iperf_test *ipt)
+{
+ return ipt->settings->rate;
+}
+
+uint64_t
+iperf_get_test_bitrate_limit(struct iperf_test *ipt)
+{
+ return ipt->settings->bitrate_limit;
+}
+
+double
+iperf_get_test_bitrate_limit_interval(struct iperf_test *ipt)
+{
+ return ipt->settings->bitrate_limit_interval;
+}
+
+int
+iperf_get_test_bitrate_limit_stats_per_interval(struct iperf_test *ipt)
+{
+ return ipt->settings->bitrate_limit_stats_per_interval;
+}
+
+uint64_t
+iperf_get_test_fqrate(struct iperf_test *ipt)
+{
+ return ipt->settings->fqrate;
+}
+
+int
+iperf_get_test_pacing_timer(struct iperf_test *ipt)
+{
+ return ipt->settings->pacing_timer;
+}
+
+uint64_t
+iperf_get_test_bytes(struct iperf_test *ipt)
+{
+ return (uint64_t) ipt->settings->bytes;
+}
+
+uint64_t
+iperf_get_test_blocks(struct iperf_test *ipt)
+{
+ return (uint64_t) ipt->settings->blocks;
+}
+
+int
+iperf_get_test_burst(struct iperf_test *ipt)
+{
+ return ipt->settings->burst;
+}
+
+char
+iperf_get_test_role(struct iperf_test *ipt)
+{
+ return ipt->role;
+}
+
+int
+iperf_get_test_reverse(struct iperf_test *ipt)
+{
+ return ipt->reverse;
+}
+
+int
+iperf_get_test_bidirectional(struct iperf_test *ipt)
+{
+ return ipt->bidirectional;
+}
+
+int
+iperf_get_test_blksize(struct iperf_test *ipt)
+{
+ return ipt->settings->blksize;
+}
+
+FILE *
+iperf_get_test_outfile (struct iperf_test *ipt)
+{
+ return ipt->outfile;
+}
+
+int
+iperf_get_test_socket_bufsize(struct iperf_test *ipt)
+{
+ return ipt->settings->socket_bufsize;
+}
+
+double
+iperf_get_test_reporter_interval(struct iperf_test *ipt)
+{
+ return ipt->reporter_interval;
+}
+
+double
+iperf_get_test_stats_interval(struct iperf_test *ipt)
+{
+ return ipt->stats_interval;
+}
+
+int
+iperf_get_test_num_streams(struct iperf_test *ipt)
+{
+ return ipt->num_streams;
+}
+
+int
+iperf_get_test_timestamps(struct iperf_test *ipt)
+{
+ return ipt->timestamps;
+}
+
+const char *
+iperf_get_test_timestamp_format(struct iperf_test *ipt)
+{
+ return ipt->timestamp_format;
+}
+
+int
+iperf_get_test_repeating_payload(struct iperf_test *ipt)
+{
+ return ipt->repeating_payload;
+}
+
+int
+iperf_get_test_bind_port(struct iperf_test *ipt)
+{
+ return ipt->bind_port;
+}
+
+int
+iperf_get_test_server_port(struct iperf_test *ipt)
+{
+ return ipt->server_port;
+}
+
+char*
+iperf_get_test_server_hostname(struct iperf_test *ipt)
+{
+ return ipt->server_hostname;
+}
+
+char*
+iperf_get_test_template(struct iperf_test *ipt)
+{
+ return ipt->tmp_template;
+}
+
+int
+iperf_get_test_protocol_id(struct iperf_test *ipt)
+{
+ return ipt->protocol->id;
+}
+
+int
+iperf_get_test_json_output(struct iperf_test *ipt)
+{
+ return ipt->json_output;
+}
+
+char *
+iperf_get_test_json_output_string(struct iperf_test *ipt)
+{
+ return ipt->json_output_string;
+}
+
+int
+iperf_get_test_zerocopy(struct iperf_test *ipt)
+{
+ return ipt->zerocopy;
+}
+
+int
+iperf_get_test_get_server_output(struct iperf_test *ipt)
+{
+ return ipt->get_server_output;
+}
+
+char
+iperf_get_test_unit_format(struct iperf_test *ipt)
+{
+ return ipt->settings->unit_format;
+}
+
+char *
+iperf_get_test_bind_address(struct iperf_test *ipt)
+{
+ return ipt->bind_address;
+}
+
+char *
+iperf_get_test_bind_dev(struct iperf_test *ipt)
+{
+ return ipt->bind_dev;
+}
+
+int
+iperf_get_test_udp_counters_64bit(struct iperf_test *ipt)
+{
+ return ipt->udp_counters_64bit;
+}
+
+int
+iperf_get_test_one_off(struct iperf_test *ipt)
+{
+ return ipt->one_off;
+}
+
+int
+iperf_get_test_tos(struct iperf_test *ipt)
+{
+ return ipt->settings->tos;
+}
+
+char *
+iperf_get_test_extra_data(struct iperf_test *ipt)
+{
+ return ipt->extra_data;
+}
+
+static const char iperf_version[] = IPERF_VERSION;
+char *
+iperf_get_iperf_version(void)
+{
+ return (char*)iperf_version;
+}
+
+int
+iperf_get_test_no_delay(struct iperf_test *ipt)
+{
+ return ipt->no_delay;
+}
+
+int
+iperf_get_test_connect_timeout(struct iperf_test *ipt)
+{
+ return ipt->settings->connect_timeout;
+}
+
+int
+iperf_get_test_idle_timeout(struct iperf_test *ipt)
+{
+ return ipt->settings->idle_timeout;
+}
+
+int
+iperf_get_dont_fragment(struct iperf_test *ipt)
+{
+ return ipt->settings->dont_fragment;
+}
+
+struct iperf_time*
+iperf_get_test_rcv_timeout(struct iperf_test *ipt)
+{
+ return &ipt->settings->rcv_timeout;
+}
+
+char*
+iperf_get_test_congestion_control(struct iperf_test* ipt)
+{
+ return ipt->congestion;
+}
+
+int
+iperf_get_test_mss(struct iperf_test *ipt)
+{
+ return ipt->settings->mss;
+}
+
+int
+iperf_get_mapped_v4(struct iperf_test* ipt)
+{
+ return ipt->mapped_v4;
+}
+
+/************** Setter routines for some fields inside iperf_test *************/
+
+void
+iperf_set_verbose(struct iperf_test *ipt, int verbose)
+{
+ ipt->verbose = verbose;
+}
+
+void
+iperf_set_control_socket(struct iperf_test *ipt, int ctrl_sck)
+{
+ ipt->ctrl_sck = ctrl_sck;
+}
+
+void
+iperf_set_test_omit(struct iperf_test *ipt, int omit)
+{
+ ipt->omit = omit;
+}
+
+void
+iperf_set_test_duration(struct iperf_test *ipt, int duration)
+{
+ ipt->duration = duration;
+}
+
+void
+iperf_set_test_reporter_interval(struct iperf_test *ipt, double reporter_interval)
+{
+ ipt->reporter_interval = reporter_interval;
+}
+
+void
+iperf_set_test_stats_interval(struct iperf_test *ipt, double stats_interval)
+{
+ ipt->stats_interval = stats_interval;
+}
+
+void
+iperf_set_test_state(struct iperf_test *ipt, signed char state)
+{
+ ipt->state = state;
+}
+
+void
+iperf_set_test_blksize(struct iperf_test *ipt, int blksize)
+{
+ ipt->settings->blksize = blksize;
+}
+
+void
+iperf_set_test_logfile(struct iperf_test *ipt, const char *logfile)
+{
+ ipt->logfile = strdup(logfile);
+}
+
+void
+iperf_set_test_rate(struct iperf_test *ipt, uint64_t rate)
+{
+ ipt->settings->rate = rate;
+}
+
+void
+iperf_set_test_bitrate_limit_maximum(struct iperf_test *ipt, uint64_t total_rate)
+{
+ ipt->settings->bitrate_limit = total_rate;
+}
+
+void
+iperf_set_test_bitrate_limit_interval(struct iperf_test *ipt, uint64_t bitrate_limit_interval)
+{
+ ipt->settings->bitrate_limit_interval = bitrate_limit_interval;
+}
+
+void
+iperf_set_test_bitrate_limit_stats_per_interval(struct iperf_test *ipt, uint64_t bitrate_limit_stats_per_interval)
+{
+ ipt->settings->bitrate_limit_stats_per_interval = bitrate_limit_stats_per_interval;
+}
+
+void
+iperf_set_test_fqrate(struct iperf_test *ipt, uint64_t fqrate)
+{
+ ipt->settings->fqrate = fqrate;
+}
+
+void
+iperf_set_test_pacing_timer(struct iperf_test *ipt, int pacing_timer)
+{
+ ipt->settings->pacing_timer = pacing_timer;
+}
+
+void
+iperf_set_test_bytes(struct iperf_test *ipt, uint64_t bytes)
+{
+ ipt->settings->bytes = (iperf_size_t) bytes;
+}
+
+void
+iperf_set_test_blocks(struct iperf_test *ipt, uint64_t blocks)
+{
+ ipt->settings->blocks = (iperf_size_t) blocks;
+}
+
+void
+iperf_set_test_burst(struct iperf_test *ipt, int burst)
+{
+ ipt->settings->burst = burst;
+}
+
+void
+iperf_set_test_bind_port(struct iperf_test *ipt, int bind_port)
+{
+ ipt->bind_port = bind_port;
+}
+
+void
+iperf_set_test_server_port(struct iperf_test *ipt, int srv_port)
+{
+ ipt->server_port = srv_port;
+}
+
+void
+iperf_set_test_socket_bufsize(struct iperf_test *ipt, int socket_bufsize)
+{
+ ipt->settings->socket_bufsize = socket_bufsize;
+}
+
+void
+iperf_set_test_num_streams(struct iperf_test *ipt, int num_streams)
+{
+ ipt->num_streams = num_streams;
+}
+
+void
+iperf_set_test_repeating_payload(struct iperf_test *ipt, int repeating_payload)
+{
+ ipt->repeating_payload = repeating_payload;
+}
+
+void
+iperf_set_test_timestamps(struct iperf_test *ipt, int timestamps)
+{
+ ipt->timestamps = timestamps;
+}
+
+void
+iperf_set_test_timestamp_format(struct iperf_test *ipt, const char *tf)
+{
+ ipt->timestamp_format = strdup(tf);
+}
+
+void
+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)())
+{
+ ipt->on_new_stream = callback;
+}
+
+void
+iperf_set_on_test_start_callback(struct iperf_test* ipt, void (*callback)())
+{
+ ipt->on_test_start = callback;
+}
+
+void
+iperf_set_on_test_connect_callback(struct iperf_test* ipt, void (*callback)())
+{
+ ipt->on_connect = callback;
+}
+
+void
+iperf_set_on_test_finish_callback(struct iperf_test* ipt, void (*callback)())
+{
+ ipt->on_test_finish = callback;
+}
+
+static void
+check_sender_has_retransmits(struct iperf_test *ipt)
+{
+ if (ipt->mode != RECEIVER && ipt->protocol->id == Ptcp && has_tcpinfo_retransmits())
+ ipt->sender_has_retransmits = 1;
+ else
+ ipt->sender_has_retransmits = 0;
+}
+
+void
+iperf_set_test_role(struct iperf_test *ipt, char role)
+{
+ ipt->role = role;
+ if (!ipt->reverse) {
+ if (ipt->bidirectional)
+ ipt->mode = BIDIRECTIONAL;
+ else if (role == 'c')
+ ipt->mode = SENDER;
+ else if (role == 's')
+ ipt->mode = RECEIVER;
+ } else {
+ if (role == 'c')
+ ipt->mode = RECEIVER;
+ else if (role == 's')
+ ipt->mode = SENDER;
+ }
+ check_sender_has_retransmits(ipt);
+}
+
+void
+iperf_set_test_server_hostname(struct iperf_test *ipt, const char *server_hostname)
+{
+ ipt->server_hostname = strdup(server_hostname);
+}
+
+void
+iperf_set_test_template(struct iperf_test *ipt, const char *tmp_template)
+{
+ ipt->tmp_template = strdup(tmp_template);
+}
+
+void
+iperf_set_test_reverse(struct iperf_test *ipt, int reverse)
+{
+ ipt->reverse = reverse;
+ if (!ipt->reverse) {
+ if (ipt->role == 'c')
+ ipt->mode = SENDER;
+ else if (ipt->role == 's')
+ ipt->mode = RECEIVER;
+ } else {
+ if (ipt->role == 'c')
+ ipt->mode = RECEIVER;
+ else if (ipt->role == 's')
+ ipt->mode = SENDER;
+ }
+ check_sender_has_retransmits(ipt);
+}
+
+void
+iperf_set_test_json_output(struct iperf_test *ipt, int json_output)
+{
+ ipt->json_output = json_output;
+}
+
+int
+iperf_has_zerocopy( void )
+{
+ return has_sendfile();
+}
+
+void
+iperf_set_test_zerocopy(struct iperf_test *ipt, int zerocopy)
+{
+ ipt->zerocopy = (zerocopy && has_sendfile());
+}
+
+void
+iperf_set_test_get_server_output(struct iperf_test *ipt, int get_server_output)
+{
+ ipt->get_server_output = get_server_output;
+}
+
+void
+iperf_set_test_unit_format(struct iperf_test *ipt, char unit_format)
+{
+ ipt->settings->unit_format = unit_format;
+}
+
+#if defined(HAVE_SSL)
+void
+iperf_set_test_client_username(struct iperf_test *ipt, const char *client_username)
+{
+ ipt->settings->client_username = strdup(client_username);
+}
+
+void
+iperf_set_test_client_password(struct iperf_test *ipt, const char *client_password)
+{
+ ipt->settings->client_password = strdup(client_password);
+}
+
+void
+iperf_set_test_client_rsa_pubkey(struct iperf_test *ipt, const char *client_rsa_pubkey_base64)
+{
+ ipt->settings->client_rsa_pubkey = load_pubkey_from_base64(client_rsa_pubkey_base64);
+}
+
+void
+iperf_set_test_server_authorized_users(struct iperf_test *ipt, const char *server_authorized_users)
+{
+ ipt->server_authorized_users = strdup(server_authorized_users);
+}
+
+void
+iperf_set_test_server_skew_threshold(struct iperf_test *ipt, int server_skew_threshold)
+{
+ ipt->server_skew_threshold = server_skew_threshold;
+}
+
+void
+iperf_set_test_server_rsa_privkey(struct iperf_test *ipt, const char *server_rsa_privkey_base64)
+{
+ ipt->server_rsa_private_key = load_privkey_from_base64(server_rsa_privkey_base64);
+}
+#endif // HAVE_SSL
+
+void
+iperf_set_test_bind_address(struct iperf_test *ipt, const char *bnd_address)
+{
+ ipt->bind_address = strdup(bnd_address);
+}
+
+void
+iperf_set_test_bind_dev(struct iperf_test *ipt, const char *bnd_dev)
+{
+ ipt->bind_dev = strdup(bnd_dev);
+}
+
+void
+iperf_set_test_udp_counters_64bit(struct iperf_test *ipt, int udp_counters_64bit)
+{
+ ipt->udp_counters_64bit = udp_counters_64bit;
+}
+
+void
+iperf_set_test_one_off(struct iperf_test *ipt, int one_off)
+{
+ ipt->one_off = one_off;
+}
+
+void
+iperf_set_test_tos(struct iperf_test *ipt, int tos)
+{
+ ipt->settings->tos = tos;
+}
+
+void
+iperf_set_test_extra_data(struct iperf_test *ipt, const char *dat)
+{
+ ipt->extra_data = strdup(dat);
+}
+
+void
+iperf_set_test_bidirectional(struct iperf_test* ipt, int bidirectional)
+{
+ ipt->bidirectional = bidirectional;
+ if (bidirectional)
+ ipt->mode = BIDIRECTIONAL;
+ else
+ iperf_set_test_reverse(ipt, ipt->reverse);
+}
+
+void
+iperf_set_test_no_delay(struct iperf_test* ipt, int no_delay)
+{
+ ipt->no_delay = no_delay;
+}
+
+void
+iperf_set_test_connect_timeout(struct iperf_test* ipt, int ct)
+{
+ ipt->settings->connect_timeout = ct;
+}
+
+void
+iperf_set_test_idle_timeout(struct iperf_test* ipt, int to)
+{
+ ipt->settings->idle_timeout = to;
+}
+
+void
+iperf_set_dont_fragment(struct iperf_test* ipt, int dnf)
+{
+ ipt->settings->dont_fragment = dnf;
+}
+
+void
+iperf_set_test_rcv_timeout(struct iperf_test* ipt, struct iperf_time* to)
+{
+ ipt->settings->rcv_timeout.secs = to->secs;
+ ipt->settings->rcv_timeout.usecs = to->usecs;
+}
+
+void
+iperf_set_test_congestion_control(struct iperf_test* ipt, char* cc)
+{
+ ipt->congestion = strdup(cc);
+}
+
+void
+iperf_set_test_mss(struct iperf_test *ipt, int mss)
+{
+ ipt->settings->mss = mss;
+}
+
+/********************** Get/set test protocol structure ***********************/
+
+struct protocol *
+get_protocol(struct iperf_test *test, int prot_id)
+{
+ struct protocol *prot;
+
+ SLIST_FOREACH(prot, &test->protocols, protocols) {
+ if (prot->id == prot_id)
+ break;
+ }
+
+ if (prot == NULL)
+ i_errno = IEPROTOCOL;
+
+ return prot;
+}
+
+int
+set_protocol(struct iperf_test *test, int prot_id)
+{
+ struct protocol *prot = NULL;
+
+ SLIST_FOREACH(prot, &test->protocols, protocols) {
+ if (prot->id == prot_id) {
+ test->protocol = prot;
+ check_sender_has_retransmits(test);
+ return 0;
+ }
+ }
+
+ i_errno = IEPROTOCOL;
+ return -1;
+}
+
+
+/************************** Iperf callback functions **************************/
+
+void
+iperf_on_new_stream(struct iperf_stream *sp)
+{
+ connect_msg(sp);
+}
+
+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));
+ } else {
+ if (test->verbose) {
+ if (test->settings->bytes)
+ iperf_printf(test, test_start_bytes, test->protocol->name, test->num_streams, test->settings->blksize, test->omit, test->settings->bytes, test->settings->tos);
+ else if (test->settings->blocks)
+ iperf_printf(test, test_start_blocks, test->protocol->name, test->num_streams, test->settings->blksize, test->omit, test->settings->blocks, test->settings->tos);
+ else
+ iperf_printf(test, test_start_time, test->protocol->name, test->num_streams, test->settings->blksize, test->omit, test->duration, test->settings->tos);
+ }
+ }
+}
+
+/* This converts an IPv6 string address from IPv4-mapped format into regular
+** old IPv4 format, which is easier on the eyes of network veterans.
+**
+** If the v6 address is not v4-mapped it is left alone.
+**
+** Returns 1 if the v6 address is v4-mapped, 0 otherwise.
+*/
+static int
+mapped_v4_to_regular_v4(char *str)
+{
+ char *prefix = "::ffff:";
+ int prefix_len;
+
+ prefix_len = strlen(prefix);
+ if (strncmp(str, prefix, prefix_len) == 0) {
+ int str_len = strlen(str);
+ memmove(str, str + prefix_len, str_len - prefix_len + 1);
+ return 1;
+ }
+ return 0;
+}
+
+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];
+ int port;
+ struct sockaddr_storage sa;
+ struct sockaddr_in *sa_inP;
+ struct sockaddr_in6 *sa_in6P;
+ socklen_t len;
+
+ 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));
+ else if (test->verbose)
+ iperf_printf(test, report_time, now_str);
+
+ if (test->role == 'c') {
+ if (test->json_output)
+ cJSON_AddItemToObject(test->json_start, "connecting_to", iperf_json_printf("host: %s port: %d", test->server_hostname, (int64_t) test->server_port));
+ else {
+ iperf_printf(test, report_connecting, test->server_hostname, test->server_port);
+ if (test->reverse)
+ iperf_printf(test, report_reverse, test->server_hostname);
+ }
+ } else {
+ len = sizeof(sa);
+ getpeername(test->ctrl_sck, (struct sockaddr *) &sa, &len);
+ if (getsockdomain(test->ctrl_sck) == AF_INET) {
+ sa_inP = (struct sockaddr_in *) &sa;
+ inet_ntop(AF_INET, &sa_inP->sin_addr, ipr, sizeof(ipr));
+ port = ntohs(sa_inP->sin_port);
+ } else {
+ sa_in6P = (struct sockaddr_in6 *) &sa;
+ inet_ntop(AF_INET6, &sa_in6P->sin6_addr, ipr, sizeof(ipr));
+ port = ntohs(sa_in6P->sin6_port);
+ }
+ if (mapped_v4_to_regular_v4(ipr)) {
+ iperf_set_mapped_v4(test, 1);
+ }
+ if (test->json_output)
+ cJSON_AddItemToObject(test->json_start, "accepted_connection", iperf_json_printf("host: %s port: %d", ipr, (int64_t) port));
+ else
+ iperf_printf(test, report_accepted, ipr, port);
+ }
+ if (test->json_output) {
+ cJSON_AddStringToObject(test->json_start, "cookie", test->cookie);
+ if (test->protocol->id == SOCK_STREAM) {
+ if (test->settings->mss)
+ cJSON_AddNumberToObject(test->json_start, "tcp_mss", test->settings->mss);
+ else {
+ cJSON_AddNumberToObject(test->json_start, "tcp_mss_default", test->ctrl_sck_mss);
+ }
+ }
+ // Duplicate to make sure it appears on all output
+ cJSON_AddNumberToObject(test->json_start, "target_bitrate", test->settings->rate);
+ cJSON_AddNumberToObject(test->json_start, "fq_rate", test->settings->fqrate);
+ } else if (test->verbose) {
+ iperf_printf(test, report_cookie, test->cookie);
+ if (test->protocol->id == SOCK_STREAM) {
+ if (test->settings->mss)
+ iperf_printf(test, " TCP MSS: %d\n", test->settings->mss);
+ else {
+ iperf_printf(test, " TCP MSS: %d (default)\n", test->ctrl_sck_mss);
+ }
+ }
+ if (test->settings->rate)
+ iperf_printf(test, " Target Bitrate: %"PRIu64"\n", test->settings->rate);
+ }
+}
+
+void
+iperf_on_test_finish(struct iperf_test *test)
+{
+}
+
+
+/******************************************************************************/
+
+/*
+ * iperf_parse_hostname tries to split apart a string into hostname %
+ * interface parts, which are returned in **p and **p1, if they
+ * exist. If the %interface part is detected, and it's not an IPv6
+ * link local address, then returns 1, else returns 0.
+ *
+ * Modifies the string pointed to by spec in-place due to the use of
+ * strtok(3). The caller should strdup(3) or otherwise copy the string
+ * if an unmodified copy is needed.
+ */
+int
+iperf_parse_hostname(struct iperf_test *test, char *spec, char **p, char **p1) {
+ struct in6_addr ipv6_addr;
+
+ // Format is <addr>[%<device>]
+ if ((*p = strtok(spec, "%")) != NULL &&
+ (*p1 = strtok(NULL, "%")) != NULL) {
+
+ /*
+ * If an IPv6 literal for a link-local address, then
+ * tell the caller to leave the "%" in the hostname.
+ */
+ if (inet_pton(AF_INET6, *p, &ipv6_addr) == 1 &&
+ IN6_IS_ADDR_LINKLOCAL(&ipv6_addr)) {
+ if (test->debug) {
+ iperf_printf(test, "IPv6 link-local address literal detected\n");
+ }
+ return 0;
+ }
+ /*
+ * Other kind of address or FQDN. The interface name after
+ * "%" is a shorthand for --bind-dev.
+ */
+ else {
+ if (test->debug) {
+ iperf_printf(test, "p %s p1 %s\n", *p, *p1);
+ }
+ return 1;
+ }
+ }
+ else {
+ if (test->debug) {
+ iperf_printf(test, "noparse\n");
+ }
+ return 0;
+ }
+}
+
+int
+iperf_parse_arguments(struct iperf_test *test, int argc, char **argv)
+{
+ static struct option longopts[] =
+ {
+ {"port", required_argument, NULL, 'p'},
+ {"format", required_argument, NULL, 'f'},
+ {"interval", required_argument, NULL, 'i'},
+ {"daemon", no_argument, NULL, 'D'},
+ {"one-off", no_argument, NULL, '1'},
+ {"verbose", no_argument, NULL, 'V'},
+ {"json", no_argument, NULL, 'J'},
+ {"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},
+ {"time", required_argument, NULL, 't'},
+ {"bytes", required_argument, NULL, 'n'},
+ {"blockcount", required_argument, NULL, 'k'},
+ {"length", required_argument, NULL, 'l'},
+ {"parallel", required_argument, NULL, 'P'},
+ {"reverse", no_argument, NULL, 'R'},
+ {"bidir", no_argument, NULL, OPT_BIDIRECTIONAL},
+ {"window", required_argument, NULL, 'w'},
+ {"bind", required_argument, NULL, 'B'},
+#if defined(HAVE_SO_BINDTODEVICE)
+ {"bind-dev", required_argument, NULL, OPT_BIND_DEV},
+#endif /* HAVE_SO_BINDTODEVICE */
+ {"cport", required_argument, NULL, OPT_CLIENT_PORT},
+ {"set-mss", required_argument, NULL, 'M'},
+ {"no-delay", no_argument, NULL, 'N'},
+ {"version4", no_argument, NULL, '4'},
+ {"version6", no_argument, NULL, '6'},
+ {"tos", required_argument, NULL, 'S'},
+ {"dscp", required_argument, NULL, OPT_DSCP},
+ {"extra-data", required_argument, NULL, OPT_EXTRA_DATA},
+#if defined(HAVE_FLOWLABEL)
+ {"flowlabel", required_argument, NULL, 'L'},
+#endif /* HAVE_FLOWLABEL */
+ {"zerocopy", no_argument, NULL, 'Z'},
+ {"omit", required_argument, NULL, 'O'},
+ {"file", required_argument, NULL, 'F'},
+ {"repeating-payload", no_argument, NULL, OPT_REPEATING_PAYLOAD},
+ {"timestamps", optional_argument, NULL, OPT_TIMESTAMPS},
+#if defined(HAVE_CPU_AFFINITY)
+ {"affinity", required_argument, NULL, 'A'},
+#endif /* HAVE_CPU_AFFINITY */
+ {"title", required_argument, NULL, 'T'},
+#if defined(HAVE_TCP_CONGESTION)
+ {"congestion", required_argument, NULL, 'C'},
+ {"linux-congestion", required_argument, NULL, 'C'},
+#endif /* HAVE_TCP_CONGESTION */
+#if defined(HAVE_SCTP_H)
+ {"sctp", no_argument, NULL, OPT_SCTP},
+ {"nstreams", required_argument, NULL, OPT_NUMSTREAMS},
+ {"xbind", required_argument, NULL, 'X'},
+#endif
+ {"pidfile", required_argument, NULL, 'I'},
+ {"logfile", required_argument, NULL, OPT_LOGFILE},
+ {"forceflush", no_argument, NULL, OPT_FORCEFLUSH},
+ {"get-server-output", no_argument, NULL, OPT_GET_SERVER_OUTPUT},
+ {"udp-counters-64bit", no_argument, NULL, OPT_UDP_COUNTERS_64BIT},
+ {"no-fq-socket-pacing", no_argument, NULL, OPT_NO_FQ_SOCKET_PACING},
+#if defined(HAVE_DONT_FRAGMENT)
+ {"dont-fragment", no_argument, NULL, OPT_DONT_FRAGMENT},
+#endif /* HAVE_DONT_FRAGMENT */
+#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},
+#endif /* HAVE_SSL */
+ {"fq-rate", required_argument, NULL, OPT_FQ_RATE},
+ {"pacing-timer", required_argument, NULL, OPT_PACING_TIMER},
+ {"connect-timeout", required_argument, NULL, OPT_CONNECT_TIMEOUT},
+ {"idle-timeout", required_argument, NULL, OPT_IDLE_TIMEOUT},
+ {"rcv-timeout", required_argument, NULL, OPT_RCV_TIMEOUT},
+ {"snd-timeout", required_argument, NULL, OPT_SND_TIMEOUT},
+ {"debug", optional_argument, NULL, 'd'},
+ {"help", no_argument, NULL, 'h'},
+ {NULL, 0, NULL, 0}
+ };
+ int flag;
+ int portno;
+ int blksize;
+ int server_flag, client_flag, rate_flag, duration_flag, rcv_timeout_flag, snd_timeout_flag;
+ char *endptr;
+#if defined(HAVE_CPU_AFFINITY)
+ char* comma;
+#endif /* HAVE_CPU_AFFINITY */
+ char* slash;
+ char *p, *p1;
+ struct xbind_entry *xbe;
+ double farg;
+ int rcv_timeout_in = 0;
+
+ blksize = 0;
+ server_flag = client_flag = rate_flag = duration_flag = rcv_timeout_flag = snd_timeout_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) {
+ switch (flag) {
+ case 'p':
+ portno = atoi(optarg);
+ if (portno < 1 || portno > 65535) {
+ i_errno = IEBADPORT;
+ return -1;
+ }
+ test->server_port = portno;
+ break;
+ case 'f':
+ if (!optarg) {
+ i_errno = IEBADFORMAT;
+ return -1;
+ }
+ test->settings->unit_format = *optarg;
+ if (test->settings->unit_format == 'k' ||
+ test->settings->unit_format == 'K' ||
+ test->settings->unit_format == 'm' ||
+ test->settings->unit_format == 'M' ||
+ test->settings->unit_format == 'g' ||
+ test->settings->unit_format == 'G' ||
+ test->settings->unit_format == 't' ||
+ test->settings->unit_format == 'T') {
+ break;
+ }
+ else {
+ i_errno = IEBADFORMAT;
+ return -1;
+ }
+ break;
+ case 'i':
+ /* XXX: could potentially want separate stat collection and reporting intervals,
+ but just set them to be the same for now */
+ test->stats_interval = test->reporter_interval = atof(optarg);
+ if ((test->stats_interval < MIN_INTERVAL || test->stats_interval > MAX_INTERVAL) && test->stats_interval != 0) {
+ i_errno = IEINTERVAL;
+ return -1;
+ }
+ break;
+ case 'D':
+ test->daemon = 1;
+ server_flag = 1;
+ break;
+ case '1':
+ test->one_off = 1;
+ server_flag = 1;
+ break;
+ case 'V':
+ test->verbose = 1;
+ break;
+ case 'J':
+ test->json_output = 1;
+ break;
+ case 'v':
+ printf("%s (cJSON %s)\n%s\n%s\n", version, cJSON_Version(), get_system_info(),
+ get_optional_features());
+ exit(0);
+ case 's':
+ if (test->role == 'c') {
+ i_errno = IESERVCLIENT;
+ return -1;
+ }
+ iperf_set_test_role(test, 's');
+ break;
+ case 'c':
+ if (test->role == 's') {
+ i_errno = IESERVCLIENT;
+ return -1;
+ }
+ 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)
+ /* 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 */
+ i_errno = IEBINDDEVNOSUPPORT;
+ return -1;
+#endif /* HAVE_SO_BINDTODEVICE */
+ }
+ break;
+ case 'u':
+ set_protocol(test, Pudp);
+ client_flag = 1;
+ break;
+ case OPT_SCTP:
+#if defined(HAVE_SCTP_H)
+ set_protocol(test, Psctp);
+ client_flag = 1;
+ break;
+#else /* HAVE_SCTP_H */
+ i_errno = IEUNIMP;
+ return -1;
+#endif /* HAVE_SCTP_H */
+
+ case OPT_NUMSTREAMS:
+#if defined(linux) || defined(__FreeBSD__)
+ test->settings->num_ostreams = unit_atoi(optarg);
+ client_flag = 1;
+#else /* linux */
+ i_errno = IEUNIMP;
+ return -1;
+#endif /* linux */
+ case 'b':
+ slash = strchr(optarg, '/');
+ if (slash) {
+ *slash = '\0';
+ ++slash;
+ test->settings->burst = atoi(slash);
+ if (test->settings->burst <= 0 ||
+ test->settings->burst > MAX_BURST) {
+ i_errno = IEBURST;
+ return -1;
+ }
+ }
+ test->settings->rate = unit_atof_rate(optarg);
+ rate_flag = 1;
+ client_flag = 1;
+ break;
+ case OPT_SERVER_BITRATE_LIMIT:
+ slash = strchr(optarg, '/');
+ if (slash) {
+ *slash = '\0';
+ ++slash;
+ test->settings->bitrate_limit_interval = atof(slash);
+ if (test->settings->bitrate_limit_interval != 0 && /* Using same Max/Min limits as for Stats Interval */
+ (test->settings->bitrate_limit_interval < MIN_INTERVAL || test->settings->bitrate_limit_interval > MAX_INTERVAL) ) {
+ i_errno = IETOTALINTERVAL;
+ return -1;
+ }
+ }
+ test->settings->bitrate_limit = unit_atof_rate(optarg);
+ server_flag = 1;
+ break;
+ case 't':
+ test->duration = atoi(optarg);
+ if (test->duration > MAX_TIME) {
+ i_errno = IEDURATION;
+ return -1;
+ }
+ duration_flag = 1;
+ client_flag = 1;
+ break;
+ case 'n':
+ test->settings->bytes = unit_atoi(optarg);
+ client_flag = 1;
+ break;
+ case 'k':
+ test->settings->blocks = unit_atoi(optarg);
+ client_flag = 1;
+ break;
+ case 'l':
+ blksize = unit_atoi(optarg);
+ client_flag = 1;
+ break;
+ case 'P':
+ test->num_streams = atoi(optarg);
+ if (test->num_streams > MAX_STREAMS) {
+ i_errno = IENUMSTREAMS;
+ return -1;
+ }
+ client_flag = 1;
+ break;
+ case 'R':
+ if (test->bidirectional) {
+ i_errno = IEREVERSEBIDIR;
+ return -1;
+ }
+ iperf_set_test_reverse(test, 1);
+ client_flag = 1;
+ break;
+ case OPT_BIDIRECTIONAL:
+ if (test->reverse) {
+ i_errno = IEREVERSEBIDIR;
+ return -1;
+ }
+ iperf_set_test_bidirectional(test, 1);
+ client_flag = 1;
+ break;
+ case 'w':
+ // XXX: This is a socket buffer, not specific to TCP
+ // Do sanity checks as double-precision floating point
+ // to avoid possible integer overflows.
+ farg = unit_atof(optarg);
+ if (farg > (double) MAX_TCP_BUFFER) {
+ i_errno = IEBUFSIZE;
+ return -1;
+ }
+ test->settings->socket_bufsize = (int) farg;
+ client_flag = 1;
+ break;
+
+ case 'B':
+ iperf_set_test_bind_address(test, optarg);
+
+ if (iperf_parse_hostname(test, optarg, &p, &p1)) {
+#if defined(HAVE_SO_BINDTODEVICE)
+ /* 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 */
+ i_errno = IEBINDDEVNOSUPPORT;
+ return -1;
+#endif /* HAVE_SO_BINDTODEVICE */
+ }
+ break;
+#if defined (HAVE_SO_BINDTODEVICE)
+ case OPT_BIND_DEV:
+ iperf_set_test_bind_dev(test, optarg);
+ break;
+#endif /* HAVE_SO_BINDTODEVICE */
+ case OPT_CLIENT_PORT:
+ portno = atoi(optarg);
+ if (portno < 1 || portno > 65535) {
+ i_errno = IEBADPORT;
+ return -1;
+ }
+ test->bind_port = portno;
+ break;
+ case 'M':
+ test->settings->mss = atoi(optarg);
+ if (test->settings->mss > MAX_MSS) {
+ i_errno = IEMSS;
+ return -1;
+ }
+ client_flag = 1;
+ break;
+ case 'N':
+ test->no_delay = 1;
+ client_flag = 1;
+ break;
+ case '4':
+ test->settings->domain = AF_INET;
+ break;
+ case '6':
+ test->settings->domain = AF_INET6;
+ break;
+ case 'S':
+ test->settings->tos = strtol(optarg, &endptr, 0);
+ if (endptr == optarg ||
+ test->settings->tos < 0 ||
+ test->settings->tos > 255) {
+ i_errno = IEBADTOS;
+ return -1;
+ }
+ client_flag = 1;
+ break;
+ case OPT_DSCP:
+ test->settings->tos = parse_qos(optarg);
+ if(test->settings->tos < 0) {
+ i_errno = IEBADTOS;
+ return -1;
+ }
+ client_flag = 1;
+ break;
+ case OPT_EXTRA_DATA:
+ test->extra_data = strdup(optarg);
+ client_flag = 1;
+ break;
+ case 'L':
+#if defined(HAVE_FLOWLABEL)
+ test->settings->flowlabel = strtol(optarg, &endptr, 0);
+ if (endptr == optarg ||
+ test->settings->flowlabel < 1 || test->settings->flowlabel > 0xfffff) {
+ i_errno = IESETFLOW;
+ return -1;
+ }
+ client_flag = 1;
+#else /* HAVE_FLOWLABEL */
+ i_errno = IEUNIMP;
+ return -1;
+#endif /* HAVE_FLOWLABEL */
+ break;
+ case 'X':
+ xbe = (struct xbind_entry *)malloc(sizeof(struct xbind_entry));
+ if (!xbe) {
+ i_errno = IESETSCTPBINDX;
+ return -1;
+ }
+ memset(xbe, 0, sizeof(*xbe));
+ xbe->name = strdup(optarg);
+ if (!xbe->name) {
+ i_errno = IESETSCTPBINDX;
+ return -1;
+ }
+ TAILQ_INSERT_TAIL(&test->xbind_addrs, xbe, link);
+ break;
+ case 'Z':
+ if (!has_sendfile()) {
+ i_errno = IENOSENDFILE;
+ return -1;
+ }
+ test->zerocopy = 1;
+ client_flag = 1;
+ break;
+ case OPT_REPEATING_PAYLOAD:
+ test->repeating_payload = 1;
+ client_flag = 1;
+ break;
+ case OPT_TIMESTAMPS:
+ iperf_set_test_timestamps(test, 1);
+ if (optarg) {
+ iperf_set_test_timestamp_format(test, optarg);
+ }
+ else {
+ iperf_set_test_timestamp_format(test, TIMESTAMP_FORMAT);
+ }
+ break;
+ case 'O':
+ test->omit = atoi(optarg);
+ if (test->omit < 0 || test->omit > 60) {
+ i_errno = IEOMIT;
+ return -1;
+ }
+ client_flag = 1;
+ break;
+ case 'F':
+ test->diskfile_name = optarg;
+ break;
+ case OPT_IDLE_TIMEOUT:
+ test->settings->idle_timeout = atoi(optarg);
+ if (test->settings->idle_timeout < 1 || test->settings->idle_timeout > MAX_TIME) {
+ i_errno = IEIDLETIMEOUT;
+ 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) {
+ i_errno = IERCVTIMEOUT;
+ return -1;
+ }
+ test->settings->rcv_timeout.secs = rcv_timeout_in / SEC_TO_mS;
+ test->settings->rcv_timeout.usecs = (rcv_timeout_in % SEC_TO_mS) * mS_TO_US;
+ rcv_timeout_flag = 1;
+ break;
+#if defined(HAVE_TCP_USER_TIMEOUT)
+ case OPT_SND_TIMEOUT:
+ test->settings->snd_timeout = atoi(optarg);
+ if (test->settings->snd_timeout < 0 || test->settings->snd_timeout > MAX_TIME * SEC_TO_mS) {
+ i_errno = IESNDTIMEOUT;
+ return -1;
+ }
+ snd_timeout_flag = 1;
+ break;
+#endif /* HAVE_TCP_USER_TIMEOUT */
+ case 'A':
+#if defined(HAVE_CPU_AFFINITY)
+ test->affinity = strtol(optarg, &endptr, 0);
+ if (endptr == optarg ||
+ test->affinity < 0 || test->affinity > 1024) {
+ i_errno = IEAFFINITY;
+ return -1;
+ }
+ comma = strchr(optarg, ',');
+ if (comma != NULL) {
+ test->server_affinity = atoi(comma+1);
+ if (test->server_affinity < 0 || test->server_affinity > 1024) {
+ i_errno = IEAFFINITY;
+ return -1;
+ }
+ client_flag = 1;
+ }
+#else /* HAVE_CPU_AFFINITY */
+ i_errno = IEUNIMP;
+ return -1;
+#endif /* HAVE_CPU_AFFINITY */
+ break;
+ case 'T':
+ test->title = strdup(optarg);
+ client_flag = 1;
+ break;
+ case 'C':
+#if defined(HAVE_TCP_CONGESTION)
+ test->congestion = strdup(optarg);
+ client_flag = 1;
+#else /* HAVE_TCP_CONGESTION */
+ i_errno = IEUNIMP;
+ return -1;
+#endif /* HAVE_TCP_CONGESTION */
+ break;
+ case 'd':
+ test->debug = 1;
+ test->debug_level = DEBUG_LEVEL_MAX;
+ if (optarg) {
+ test->debug_level = atoi(optarg);
+ if (test->debug_level < 0)
+ test->debug_level = DEBUG_LEVEL_MAX;
+ }
+ break;
+ case 'I':
+ test->pidfile = strdup(optarg);
+ break;
+ case OPT_LOGFILE:
+ test->logfile = strdup(optarg);
+ break;
+ case OPT_FORCEFLUSH:
+ test->forceflush = 1;
+ break;
+ case OPT_GET_SERVER_OUTPUT:
+ test->get_server_output = 1;
+ client_flag = 1;
+ break;
+ case OPT_UDP_COUNTERS_64BIT:
+ test->udp_counters_64bit = 1;
+ break;
+ case OPT_NO_FQ_SOCKET_PACING:
+#if defined(HAVE_SO_MAX_PACING_RATE)
+ printf("Warning: --no-fq-socket-pacing is deprecated\n");
+ test->settings->fqrate = 0;
+ client_flag = 1;
+#else /* HAVE_SO_MAX_PACING_RATE */
+ i_errno = IEUNIMP;
+ return -1;
+#endif
+ break;
+ case OPT_FQ_RATE:
+#if defined(HAVE_SO_MAX_PACING_RATE)
+ test->settings->fqrate = unit_atof_rate(optarg);
+ client_flag = 1;
+#else /* HAVE_SO_MAX_PACING_RATE */
+ i_errno = IEUNIMP;
+ return -1;
+#endif
+ break;
+#if defined(HAVE_DONT_FRAGMENT)
+ case OPT_DONT_FRAGMENT:
+ test->settings->dont_fragment = 1;
+ client_flag = 1;
+ break;
+#endif /* HAVE_DONT_FRAGMENT */
+#if defined(HAVE_SSL)
+ case OPT_CLIENT_USERNAME:
+ client_username = strdup(optarg);
+ break;
+ case OPT_CLIENT_RSA_PUBLIC_KEY:
+ client_rsa_public_key = strdup(optarg);
+ break;
+ case OPT_SERVER_RSA_PRIVATE_KEY:
+ server_rsa_private_key = strdup(optarg);
+ break;
+ case OPT_SERVER_AUTHORIZED_USERS:
+ test->server_authorized_users = strdup(optarg);
+ break;
+ case OPT_SERVER_SKEW_THRESHOLD:
+ test->server_skew_threshold = atoi(optarg);
+ if(test->server_skew_threshold <= 0){
+ i_errno = IESKEWTHRESHOLD;
+ return -1;
+ }
+ break;
+#endif /* HAVE_SSL */
+ case OPT_PACING_TIMER:
+ test->settings->pacing_timer = unit_atoi(optarg);
+ client_flag = 1;
+ break;
+ case OPT_CONNECT_TIMEOUT:
+ test->settings->connect_timeout = unit_atoi(optarg);
+ client_flag = 1;
+ break;
+ case 'h':
+ usage_long(stdout);
+ exit(0);
+ default:
+ fprintf(stderr, "\n");
+ usage();
+ exit(1);
+ }
+ }
+
+ /* Check flag / role compatibility. */
+ if (test->role == 'c' && server_flag) {
+ i_errno = IESERVERONLY;
+ return -1;
+ }
+ if (test->role == 's' && client_flag) {
+ i_errno = IECLIENTONLY;
+ return -1;
+ }
+
+#if defined(HAVE_SSL)
+
+ if (test->role == 's' && (client_username || client_rsa_public_key)){
+ i_errno = IECLIENTONLY;
+ return -1;
+ } else if (test->role == 'c' && (client_username || client_rsa_public_key) &&
+ !(client_username && client_rsa_public_key)) {
+ i_errno = IESETCLIENTAUTH;
+ return -1;
+ } else if (test->role == 'c' && (client_username && client_rsa_public_key)){
+
+ char *client_password = NULL;
+ size_t s;
+ if (test_load_pubkey_from_file(client_rsa_public_key) < 0){
+ iperf_err(test, "%s\n", ERR_error_string(ERR_get_error(), NULL));
+ i_errno = IESETCLIENTAUTH;
+ return -1;
+ }
+ /* Need to copy env var, so we can do a common free */
+ if ((client_password = getenv("IPERF3_PASSWORD")) != NULL)
+ client_password = strdup(client_password);
+ else if (iperf_getpass(&client_password, &s, stdin) < 0){
+ i_errno = IESETCLIENTAUTH;
+ return -1;
+ }
+
+ test->settings->client_username = client_username;
+ test->settings->client_password = client_password;
+ test->settings->client_rsa_pubkey = load_pubkey_from_file(client_rsa_public_key);
+ free(client_rsa_public_key);
+ client_rsa_public_key = NULL;
+ }
+
+ if (test->role == 'c' && (server_rsa_private_key || test->server_authorized_users)){
+ i_errno = IESERVERONLY;
+ return -1;
+ } else if (test->role == 'c' && (test->server_skew_threshold != 0)){
+ i_errno = IESERVERONLY;
+ return -1;
+ } else if (test->role == 'c' && rcv_timeout_flag && test->mode == SENDER){
+ i_errno = IERVRSONLYRCVTIMEOUT;
+ return -1;
+ } else if (test->role == 's' && (server_rsa_private_key || test->server_authorized_users) &&
+ !(server_rsa_private_key && test->server_authorized_users)) {
+ i_errno = IESETSERVERAUTH;
+ return -1;
+ }
+
+ 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));
+ i_errno = IESETSERVERAUTH;
+ return -1;
+ }
+ free(server_rsa_private_key);
+ server_rsa_private_key = NULL;
+
+ if(test->server_skew_threshold == 0){
+ // Set default value for time skew threshold
+ test->server_skew_threshold=10;
+ }
+ }
+
+#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) {
+ i_errno = IEUDPFILETRANSFER;
+ return -1;
+ }
+
+ if (blksize == 0) {
+ if (test->protocol->id == Pudp)
+ blksize = 0; /* try to dynamically determine from MSS */
+ else if (test->protocol->id == Psctp)
+ blksize = DEFAULT_SCTP_BLKSIZE;
+ else
+ blksize = DEFAULT_TCP_BLKSIZE;
+ }
+ if ((test->protocol->id != Pudp && blksize <= 0)
+ || blksize > MAX_BLOCKSIZE) {
+ i_errno = IEBLOCKSIZE;
+ return -1;
+ }
+ if (test->protocol->id == Pudp &&
+ (blksize > 0 &&
+ (blksize < MIN_UDP_BLOCKSIZE || blksize > MAX_UDP_BLOCKSIZE))) {
+ i_errno = IEUDPBLOCKSIZE;
+ return -1;
+ }
+ test->settings->blksize = blksize;
+
+ if (!rate_flag)
+ test->settings->rate = test->protocol->id == Pudp ? UDP_RATE : 0;
+
+ /* if no bytes or blocks specified, nor a duration_flag, and we have -F,
+ ** get the file-size as the bytes count to be transferred
+ */
+ if (test->settings->bytes == 0 &&
+ test->settings->blocks == 0 &&
+ ! duration_flag &&
+ test->diskfile_name != (char*) 0 &&
+ test->role == 'c'
+ ){
+ struct stat st;
+ if( stat(test->diskfile_name, &st) == 0 ){
+ iperf_size_t file_bytes = st.st_size;
+ test->settings->bytes = file_bytes;
+ if (test->debug)
+ printf("End condition set to file-size: %"PRIu64" bytes\n", test->settings->bytes);
+ }
+ // if failing to read file stat, it should fallback to default duration mode
+ }
+
+ if ((test->settings->bytes != 0 || test->settings->blocks != 0) && ! duration_flag)
+ test->duration = 0;
+
+ /* Disallow specifying multiple test end conditions. The code actually
+ ** works just fine without this prohibition. As soon as any one of the
+ ** three possible end conditions is met, the test ends. So this check
+ ** could be removed if desired.
+ */
+ if ((duration_flag && test->settings->bytes != 0) ||
+ (duration_flag && test->settings->blocks != 0) ||
+ (test->settings->bytes != 0 && test->settings->blocks != 0)) {
+ i_errno = IEENDCONDITIONS;
+ return -1;
+ }
+
+ /* For subsequent calls to getopt */
+#ifdef __APPLE__
+ optreset = 1;
+#endif
+ optind = 0;
+
+ if ((test->role != 'c') && (test->role != 's')) {
+ i_errno = IENOROLE;
+ return -1;
+ }
+
+ /* Set Total-rate average interval to multiplicity of State interval */
+ if (test->settings->bitrate_limit_interval != 0) {
+ test->settings->bitrate_limit_stats_per_interval =
+ (test->settings->bitrate_limit_interval <= test->stats_interval ?
+ 1 : round(test->settings->bitrate_limit_interval/test->stats_interval) );
+ }
+
+ /* Show warning if JSON output is used with explicit report format */
+ if ((test->json_output) && (test->settings->unit_format != 'a')) {
+ warning("Report format (-f) flag ignored with JSON output (-J)");
+ }
+
+ /* Show warning if JSON output is used with verbose or debug flags */
+ if (test->json_output && test->verbose) {
+ warning("Verbose output (-v) may interfere with JSON output (-J)");
+ }
+ if (test->json_output && test->debug) {
+ warning("Debug output (-d) may interfere with JSON output (-J)");
+ }
+
+ return 0;
+}
+
+/*
+ * Open the file specified by test->logfile and set test->outfile to its' FD.
+ */
+int iperf_open_logfile(struct iperf_test *test)
+{
+ test->outfile = fopen(test->logfile, "a+");
+ if (test->outfile == NULL) {
+ i_errno = IELOGFILE;
+ return -1;
+ }
+
+ return 0;
+}
+
+void iperf_close_logfile(struct iperf_test *test)
+{
+ if (test->outfile && test->outfile != stdout) {
+ fclose(test->outfile);
+ test->outfile = NULL;
+ }
+}
+
+int
+iperf_set_send_state(struct iperf_test *test, signed char state)
+{
+ if (test->ctrl_sck >= 0) {
+ test->state = state;
+ if (Nwrite(test->ctrl_sck, (char*) &state, sizeof(state), Ptcp) < 0) {
+ i_errno = IESENDMESSAGE;
+ return -1;
+ }
+ }
+ return 0;
+}
+
+void
+iperf_check_throttle(struct iperf_stream *sp, struct iperf_time *nowP)
+{
+ struct iperf_time temp_time;
+ double seconds;
+ uint64_t bits_per_second;
+
+ 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) {
+ sp->green_light = 1;
+ } else {
+ sp->green_light = 0;
+ }
+}
+
+/* Verify that average traffic is not greater than the specified limit */
+void
+iperf_check_total_rate(struct iperf_test *test, iperf_size_t last_interval_bytes_transferred)
+{
+ double seconds;
+ uint64_t bits_per_second;
+ iperf_size_t total_bytes;
+ int i;
+
+ if (test->done || test->settings->bitrate_limit == 0) // Continue only if check should be done
+ return;
+
+ /* Add last inetrval's 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;
+
+ /* Ensure that enough stats periods passed to allow averaging throughput */
+ test->bitrate_limit_stats_count += 1;
+ if (test->bitrate_limit_stats_count < test->settings->bitrate_limit_stats_per_interval)
+ return;
+
+ /* Calculating total bytes traffic to be averaged */
+ for (i = 0, total_bytes = 0; i < test->settings->bitrate_limit_stats_per_interval; i++) {
+ total_bytes += test->bitrate_limit_intervals_traffic_bytes[i];
+ }
+
+ seconds = test->stats_interval * test->settings->bitrate_limit_stats_per_interval;
+ bits_per_second = total_bytes * 8 / seconds;
+ if (test->debug) {
+ iperf_printf(test,"Interval %" PRIu64 " - throughput %" PRIu64 " bps (limit %" PRIu64 ")\n", test->bitrate_limit_stats_count, bits_per_second, test->settings->bitrate_limit);
+ }
+
+ if (bits_per_second > test->settings->bitrate_limit) {
+ if (iperf_get_verbose(test))
+ iperf_err(test, "Total throughput of %" PRIu64 " bps exceeded %" PRIu64 " bps limit", bits_per_second, test->settings->bitrate_limit);
+ test->bitrate_limit_exceeded = 1;
+ }
+}
+
+int
+iperf_send_mt(struct iperf_stream *sp)
+{
+ register int multisend, r, streams_active;
+ register struct iperf_test *test = sp->test;
+ struct iperf_time now;
+ int no_throttle_check;
+
+ /* Can we do multisend mode? */
+ if (test->settings->burst != 0)
+ multisend = test->settings->burst;
+ else if (test->settings->rate == 0)
+ multisend = test->multisend;
+ else
+ 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;
+ {
+ if (sp->green_light && sp->sender) {
+ // 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 */
+ iperf_time_now(&now);
+ if (sp->sender)
+ iperf_check_throttle(sp, &now);
+ }
+ return 0;
+}
+
+int
+iperf_recv_mt(struct iperf_stream *sp)
+{
+ int r;
+ struct iperf_test *test = sp->test;
+
+ if ((r = sp->rcv(sp)) < 0) {
+ i_errno = IESTREAMREAD;
+ return r;
+ }
+ test->bytes_received += r;
+ ++test->blocks_received;
+
+ return 0;
+}
+
+int
+iperf_init_test(struct iperf_test *test)
+{
+ struct iperf_time now;
+ struct iperf_stream *sp;
+
+ if (test->protocol->init) {
+ if (test->protocol->init(test) < 0)
+ return -1;
+ }
+
+ /* Init each stream. */
+ if (iperf_time_now(&now) < 0) {
+ i_errno = IEINITTEST;
+ return -1;
+ }
+ SLIST_FOREACH(sp, &test->streams, streams) {
+ sp->result->start_time = sp->result->start_time_fixed = now;
+ }
+
+ if (test->on_test_start)
+ test->on_test_start(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;
+ 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;
+}
+
+#if defined(HAVE_SSL)
+int test_is_authorized(struct iperf_test *test){
+ if ( !(test->server_rsa_private_key && test->server_authorized_users)) {
+ return 0;
+ }
+
+ 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);
+ 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);
+ }
+ free(username);
+ free(password);
+ return 0;
+ } else {
+ if (test->debug) {
+ iperf_printf(test, report_authentication_failed, ret, username, ts);
+ }
+ free(username);
+ free(password);
+ return -1;
+ }
+ }
+ return -1;
+}
+#endif //HAVE_SSL
+
+/**
+ * iperf_exchange_parameters - handles the param_Exchange part for client
+ *
+ */
+
+int
+iperf_exchange_parameters(struct iperf_test *test)
+{
+ int s;
+ int32_t err;
+
+ if (test->role == 'c') {
+
+ if (send_parameters(test) < 0)
+ return -1;
+
+ } else {
+
+ 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;
+ }
+
+ FD_SET(s, &test->read_set);
+ test->max_fd = (s > test->max_fd) ? s : test->max_fd;
+ test->prot_listener = s;
+
+ // Send the control message to create streams and start the test
+ if (iperf_set_send_state(test, CREATE_STREAMS) != 0)
+ return -1;
+
+ }
+
+ return 0;
+}
+
+/*************************************************************/
+
+int
+iperf_exchange_results(struct iperf_test *test)
+{
+ if (test->role == 'c') {
+ /* Send results to server. */
+ if (send_results(test) < 0)
+ return -1;
+ /* Get server results. */
+ if (get_results(test) < 0)
+ return -1;
+ } else {
+ /* Get client results. */
+ if (get_results(test) < 0)
+ return -1;
+ /* Send results to client. */
+ if (send_results(test) < 0)
+ return -1;
+ }
+ return 0;
+}
+
+/*************************************************************/
+
+static int
+send_parameters(struct iperf_test *test)
+{
+ int r = 0;
+ cJSON *j;
+
+ j = cJSON_CreateObject();
+ if (j == NULL) {
+ i_errno = IESENDPARAMS;
+ r = -1;
+ } else {
+ if (test->protocol->id == Ptcp)
+ cJSON_AddTrueToObject(j, "tcp");
+ else if (test->protocol->id == Pudp)
+ cJSON_AddTrueToObject(j, "udp");
+ else if (test->protocol->id == Psctp)
+ cJSON_AddTrueToObject(j, "sctp");
+ cJSON_AddNumberToObject(j, "omit", test->omit);
+ if (test->server_affinity != -1)
+ cJSON_AddNumberToObject(j, "server_affinity", test->server_affinity);
+ cJSON_AddNumberToObject(j, "time", test->duration);
+ cJSON_AddNumberToObject(j, "num", test->settings->bytes);
+ cJSON_AddNumberToObject(j, "blockcount", test->settings->blocks);
+ if (test->settings->mss)
+ cJSON_AddNumberToObject(j, "MSS", test->settings->mss);
+ if (test->no_delay)
+ cJSON_AddTrueToObject(j, "nodelay");
+ cJSON_AddNumberToObject(j, "parallel", test->num_streams);
+ if (test->reverse)
+ cJSON_AddTrueToObject(j, "reverse");
+ if (test->bidirectional)
+ cJSON_AddTrueToObject(j, "bidirectional");
+ if (test->settings->socket_bufsize)
+ cJSON_AddNumberToObject(j, "window", test->settings->socket_bufsize);
+ if (test->settings->blksize)
+ cJSON_AddNumberToObject(j, "len", test->settings->blksize);
+ if (test->settings->rate)
+ cJSON_AddNumberToObject(j, "bandwidth", test->settings->rate);
+ if (test->settings->fqrate)
+ cJSON_AddNumberToObject(j, "fqrate", test->settings->fqrate);
+ if (test->settings->pacing_timer)
+ cJSON_AddNumberToObject(j, "pacing_timer", test->settings->pacing_timer);
+ if (test->settings->burst)
+ cJSON_AddNumberToObject(j, "burst", test->settings->burst);
+ if (test->settings->tos)
+ cJSON_AddNumberToObject(j, "TOS", test->settings->tos);
+ if (test->settings->flowlabel)
+ cJSON_AddNumberToObject(j, "flowlabel", test->settings->flowlabel);
+ if (test->title)
+ cJSON_AddStringToObject(j, "title", test->title);
+ if (test->extra_data)
+ cJSON_AddStringToObject(j, "extra_data", test->extra_data);
+ if (test->congestion)
+ cJSON_AddStringToObject(j, "congestion", test->congestion);
+ if (test->congestion_used)
+ cJSON_AddStringToObject(j, "congestion_used", test->congestion_used);
+ if (test->get_server_output)
+ cJSON_AddNumberToObject(j, "get_server_output", iperf_get_test_get_server_output(test));
+ if (test->udp_counters_64bit)
+ cJSON_AddNumberToObject(j, "udp_counters_64bit", iperf_get_test_udp_counters_64bit(test));
+ if (test->repeating_payload)
+ cJSON_AddNumberToObject(j, "repeating_payload", test->repeating_payload);
+ if (test->zerocopy)
+ cJSON_AddNumberToObject(j, "zerocopy", test->zerocopy);
+#if defined(HAVE_DONT_FRAGMENT)
+ if (test->settings->dont_fragment)
+ cJSON_AddNumberToObject(j, "dont_fragment", test->settings->dont_fragment);
+#endif /* HAVE_DONT_FRAGMENT */
+#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);
+
+ if (rc) {
+ cJSON_Delete(j);
+ i_errno = IESENDPARAMS;
+ return -1;
+ }
+
+ cJSON_AddStringToObject(j, "authtoken", test->settings->authtoken);
+ }
+#endif // HAVE_SSL
+ cJSON_AddStringToObject(j, "client_version", IPERF_VERSION);
+
+ if (test->debug) {
+ char *str = cJSON_Print(j);
+ printf("send_parameters:\n%s\n", str);
+ cJSON_free(str);
+ }
+
+ if (JSON_write(test->ctrl_sck, j) < 0) {
+ i_errno = IESENDPARAMS;
+ r = -1;
+ }
+ cJSON_Delete(j);
+ }
+ return r;
+}
+
+/*************************************************************/
+
+static int
+get_parameters(struct iperf_test *test)
+{
+ int r = 0;
+ cJSON *j;
+ cJSON *j_p;
+
+ j = JSON_read(test->ctrl_sck);
+ if (j == NULL) {
+ i_errno = IERECVPARAMS;
+ r = -1;
+ } else {
+ if (test->debug) {
+ char *str;
+ str = cJSON_Print(j);
+ printf("get_parameters:\n%s\n", str );
+ cJSON_free(str);
+ }
+
+ if ((j_p = cJSON_GetObjectItem(j, "tcp")) != 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)
+ set_protocol(test, Psctp);
+ if ((j_p = cJSON_GetObjectItem(j, "omit")) != NULL)
+ test->omit = j_p->valueint;
+ if ((j_p = cJSON_GetObjectItem(j, "server_affinity")) != NULL)
+ test->server_affinity = j_p->valueint;
+ if ((j_p = cJSON_GetObjectItem(j, "time")) != NULL)
+ test->duration = j_p->valueint;
+ test->settings->bytes = 0;
+ if ((j_p = cJSON_GetObjectItem(j, "num")) != NULL)
+ test->settings->bytes = j_p->valueint;
+ test->settings->blocks = 0;
+ if ((j_p = cJSON_GetObjectItem(j, "blockcount")) != NULL)
+ test->settings->blocks = j_p->valueint;
+ if ((j_p = cJSON_GetObjectItem(j, "MSS")) != NULL)
+ test->settings->mss = j_p->valueint;
+ if ((j_p = cJSON_GetObjectItem(j, "nodelay")) != NULL)
+ test->no_delay = 1;
+ if ((j_p = cJSON_GetObjectItem(j, "parallel")) != NULL)
+ test->num_streams = j_p->valueint;
+ if ((j_p = cJSON_GetObjectItem(j, "reverse")) != NULL)
+ iperf_set_test_reverse(test, 1);
+ if ((j_p = cJSON_GetObjectItem(j, "bidirectional")) != NULL)
+ iperf_set_test_bidirectional(test, 1);
+ if ((j_p = cJSON_GetObjectItem(j, "window")) != NULL)
+ test->settings->socket_bufsize = j_p->valueint;
+ if ((j_p = cJSON_GetObjectItem(j, "len")) != NULL)
+ test->settings->blksize = j_p->valueint;
+ if ((j_p = cJSON_GetObjectItem(j, "bandwidth")) != NULL)
+ test->settings->rate = j_p->valueint;
+ if ((j_p = cJSON_GetObjectItem(j, "fqrate")) != NULL)
+ test->settings->fqrate = j_p->valueint;
+ if ((j_p = cJSON_GetObjectItem(j, "pacing_timer")) != NULL)
+ test->settings->pacing_timer = j_p->valueint;
+ if ((j_p = cJSON_GetObjectItem(j, "burst")) != NULL)
+ test->settings->burst = j_p->valueint;
+ if ((j_p = cJSON_GetObjectItem(j, "TOS")) != NULL)
+ test->settings->tos = j_p->valueint;
+ if ((j_p = cJSON_GetObjectItem(j, "flowlabel")) != NULL)
+ test->settings->flowlabel = j_p->valueint;
+ if ((j_p = cJSON_GetObjectItem(j, "title")) != NULL)
+ test->title = strdup(j_p->valuestring);
+ if ((j_p = cJSON_GetObjectItem(j, "extra_data")) != NULL)
+ test->extra_data = strdup(j_p->valuestring);
+ if ((j_p = cJSON_GetObjectItem(j, "congestion")) != NULL)
+ test->congestion = strdup(j_p->valuestring);
+ if ((j_p = cJSON_GetObjectItem(j, "congestion_used")) != NULL)
+ test->congestion_used = strdup(j_p->valuestring);
+ if ((j_p = cJSON_GetObjectItem(j, "get_server_output")) != NULL)
+ iperf_set_test_get_server_output(test, 1);
+ if ((j_p = cJSON_GetObjectItem(j, "udp_counters_64bit")) != NULL)
+ iperf_set_test_udp_counters_64bit(test, 1);
+ if ((j_p = cJSON_GetObjectItem(j, "repeating_payload")) != NULL)
+ test->repeating_payload = 1;
+ if ((j_p = cJSON_GetObjectItem(j, "zerocopy")) != NULL)
+ test->zerocopy = j_p->valueint;
+#if defined(HAVE_DONT_FRAGMENT)
+ if ((j_p = cJSON_GetObjectItem(j, "dont_fragment")) != NULL)
+ test->settings->dont_fragment = j_p->valueint;
+#endif /* HAVE_DONT_FRAGMENT */
+#if defined(HAVE_SSL)
+ if ((j_p = cJSON_GetObjectItem(j, "authtoken")) != NULL)
+ test->settings->authtoken = strdup(j_p->valuestring);
+#endif //HAVE_SSL
+ 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);
+ }
+ return r;
+}
+
+/*************************************************************/
+
+static int
+send_results(struct iperf_test *test)
+{
+ int r = 0;
+ cJSON *j;
+ cJSON *j_streams;
+ struct iperf_stream *sp;
+ cJSON *j_stream;
+ int sender_has_retransmits;
+ iperf_size_t bytes_transferred;
+ int retransmits;
+ struct iperf_time temp_time;
+ double start_time, end_time;
+
+ j = cJSON_CreateObject();
+ if (j == NULL) {
+ i_errno = IEPACKAGERESULTS;
+ r = -1;
+ } else {
+ cJSON_AddNumberToObject(j, "cpu_util_total", test->cpu_util[0]);
+ cJSON_AddNumberToObject(j, "cpu_util_user", test->cpu_util[1]);
+ cJSON_AddNumberToObject(j, "cpu_util_system", test->cpu_util[2]);
+ if ( test->mode == RECEIVER )
+ sender_has_retransmits = -1;
+ else
+ sender_has_retransmits = test->sender_has_retransmits;
+ cJSON_AddNumberToObject(j, "sender_has_retransmits", sender_has_retransmits);
+ if ( test->congestion_used ) {
+ cJSON_AddStringToObject(j, "congestion_used", test->congestion_used);
+ }
+
+ /* If on the server and sending server output, then do this */
+ if (test->role == 's' && test->get_server_output) {
+ if (test->json_output) {
+ /* Add JSON output */
+ cJSON_AddItemReferenceToObject(j, "server_output_json", test->json_top);
+ }
+ else {
+ /* Add textual output */
+ size_t buflen = 0;
+
+ /* Figure out how much room we need to hold the complete output string */
+ struct iperf_textline *t;
+ TAILQ_FOREACH(t, &(test->server_output_list), textlineentries) {
+ buflen += strlen(t->line);
+ }
+
+ /* Allocate and build it up from the component lines */
+ char *output = calloc(buflen + 1, 1);
+ TAILQ_FOREACH(t, &(test->server_output_list), textlineentries) {
+ strncat(output, t->line, buflen);
+ buflen -= strlen(t->line);
+ }
+
+ cJSON_AddStringToObject(j, "server_output_text", output);
+ free(output);
+ }
+ }
+
+ j_streams = cJSON_CreateArray();
+ if (j_streams == NULL) {
+ i_errno = IEPACKAGERESULTS;
+ r = -1;
+ } else {
+ cJSON_AddItemToObject(j, "streams", j_streams);
+ SLIST_FOREACH(sp, &test->streams, streams) {
+ j_stream = cJSON_CreateObject();
+ if (j_stream == NULL) {
+ i_errno = IEPACKAGERESULTS;
+ r = -1;
+ } else {
+ cJSON_AddItemToArray(j_streams, j_stream);
+ bytes_transferred = sp->sender ? (sp->result->bytes_sent - sp->result->bytes_sent_omit) : sp->result->bytes_received;
+ retransmits = (sp->sender && test->sender_has_retransmits) ? sp->result->stream_retrans : -1;
+ cJSON_AddNumberToObject(j_stream, "id", sp->id);
+ cJSON_AddNumberToObject(j_stream, "bytes", bytes_transferred);
+ 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);
+ iperf_time_diff(&sp->result->start_time, &sp->result->end_time, &temp_time);
+ end_time = iperf_time_in_secs(&temp_time);
+ cJSON_AddNumberToObject(j_stream, "start_time", start_time);
+ cJSON_AddNumberToObject(j_stream, "end_time", end_time);
+
+ }
+ }
+ if (r == 0 && test->debug) {
+ char *str = cJSON_Print(j);
+ printf("send_results\n%s\n", str);
+ cJSON_free(str);
+ }
+ if (r == 0 && JSON_write(test->ctrl_sck, j) < 0) {
+ i_errno = IESENDRESULTS;
+ r = -1;
+ }
+ }
+ cJSON_Delete(j);
+ }
+ return r;
+}
+
+/*************************************************************/
+
+static int
+get_results(struct iperf_test *test)
+{
+ int r = 0;
+ cJSON *j;
+ cJSON *j_cpu_util_total;
+ cJSON *j_cpu_util_user;
+ cJSON *j_cpu_util_system;
+ cJSON *j_remote_congestion_used;
+ cJSON *j_sender_has_retransmits;
+ int result_has_retransmits;
+ cJSON *j_streams;
+ int n, i;
+ cJSON *j_stream;
+ cJSON *j_id;
+ cJSON *j_bytes;
+ 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;
+ int64_t cerror, pcount, omitted_cerror, omitted_pcount;
+ double jitter;
+ iperf_size_t bytes_transferred;
+ int retransmits;
+ struct iperf_stream *sp;
+
+ j = JSON_read(test->ctrl_sck);
+ 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");
+ 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;
+ } else {
+ if (test->debug) {
+ char *str = cJSON_Print(j);
+ printf("get_results\n%s\n", str);
+ cJSON_free(str);
+ }
+
+ test->remote_cpu_util[0] = j_cpu_util_total->valuedouble;
+ test->remote_cpu_util[1] = j_cpu_util_user->valuedouble;
+ test->remote_cpu_util[2] = j_cpu_util_system->valuedouble;
+ result_has_retransmits = j_sender_has_retransmits->valueint;
+ if ( test->mode == RECEIVER ) {
+ test->sender_has_retransmits = result_has_retransmits;
+ test->other_side_has_retransmits = 0;
+ }
+ else if ( test->mode == BIDIRECTIONAL )
+ test->other_side_has_retransmits = result_has_retransmits;
+
+ j_streams = cJSON_GetObjectItem(j, "streams");
+ if (j_streams == NULL) {
+ i_errno = IERECVRESULTS;
+ r = -1;
+ } else {
+ n = cJSON_GetArraySize(j_streams);
+ for (i=0; i<n; ++i) {
+ j_stream = cJSON_GetArrayItem(j_streams, i);
+ if (j_stream == NULL) {
+ 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_omitted_errors = cJSON_GetObjectItem(j_stream, "omitted_errors");
+ j_packets = cJSON_GetObjectItem(j_stream, "packets");
+ j_omitted_packets = cJSON_GetObjectItem(j_stream, "omitted_packets");
+ j_start_time = cJSON_GetObjectItem(j_stream, "start_time");
+ j_end_time = cJSON_GetObjectItem(j_stream, "end_time");
+ 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" statistcs */
+ i_errno = IERECVRESULTS;
+ r = -1;
+ } else {
+ sid = j_id->valueint;
+ bytes_transferred = j_bytes->valueint;
+ retransmits = j_retransmits->valueint;
+ 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) {
+ i_errno = IESTREAMID;
+ r = -1;
+ } else {
+ if (sp->sender) {
+ sp->jitter = jitter;
+ 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
+ * available; this is the case for older (pre-3.2)
+ * servers.
+ *
+ * We need to have result structure members to hold
+ * the both sides' start_time and end_time.
+ */
+ if (j_start_time && j_end_time) {
+ sp->result->receiver_time = j_end_time->valuedouble - j_start_time->valuedouble;
+ }
+ else {
+ sp->result->receiver_time = 0.0;
+ }
+ } else {
+ 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;
+ }
+ else {
+ sp->result->sender_time = 0.0;
+ }
+ }
+ }
+ }
+ }
+ }
+ /*
+ * If we're the client and we're supposed to get remote results,
+ * look them up and process accordingly.
+ */
+ if (test->role == 'c' && iperf_get_test_get_server_output(test)) {
+ /* Look for JSON. If we find it, grab the object so it doesn't get deleted. */
+ j_server_output = cJSON_DetachItemFromObject(j, "server_output_json");
+ if (j_server_output != NULL) {
+ test->json_server_output = j_server_output;
+ }
+ else {
+ /* No JSON, look for textual output. Make a copy of the text for later. */
+ j_server_output = cJSON_GetObjectItem(j, "server_output_text");
+ if (j_server_output != NULL) {
+ test->server_output_text = strdup(j_server_output->valuestring);
+ }
+ }
+ }
+ }
+ }
+
+ j_remote_congestion_used = cJSON_GetObjectItem(j, "congestion_used");
+ if (j_remote_congestion_used != NULL) {
+ test->remote_congestion_used = strdup(j_remote_congestion_used->valuestring);
+ }
+
+ cJSON_Delete(j);
+ }
+ return r;
+}
+
+/*************************************************************/
+
+static int
+JSON_write(int fd, cJSON *json)
+{
+ uint32_t hsize, nsize;
+ char *str;
+ int r = 0;
+
+ str = cJSON_PrintUnformatted(json);
+ if (str == NULL)
+ r = -1;
+ else {
+ hsize = strlen(str);
+ nsize = htonl(hsize);
+ if (Nwrite(fd, (char*) &nsize, sizeof(nsize), Ptcp) < 0)
+ r = -1;
+ else {
+ if (Nwrite(fd, str, hsize, Ptcp) < 0)
+ r = -1;
+ }
+ cJSON_free(str);
+ }
+ return r;
+}
+
+/*************************************************************/
+
+static cJSON *
+JSON_read(int fd)
+{
+ uint32_t hsize, nsize;
+ size_t strsize;
+ char *str;
+ cJSON *json = NULL;
+ int rc;
+
+ /*
+ * 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 */
+ 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 {
+ printf("WARNING: Size of data read does not correspond to offered length\n");
+ }
+ }
+ }
+ free(str);
+ }
+ else {
+ printf("WARNING: Data length overflow\n");
+ }
+ }
+ return json;
+}
+
+/*************************************************************/
+/**
+ * add_to_interval_list -- adds new interval to the interval_list
+ */
+
+void
+add_to_interval_list(struct iperf_stream_result * rp, struct iperf_interval_results * new)
+{
+ struct iperf_interval_results *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);
+}
+
+
+/************************************************************/
+
+/**
+ * connect_msg -- displays connection message
+ * denoting sender/receiver details
+ *
+ */
+
+void
+connect_msg(struct iperf_stream *sp)
+{
+ char ipl[INET6_ADDRSTRLEN], ipr[INET6_ADDRSTRLEN];
+ int lport, rport;
+
+ if (getsockdomain(sp->socket) == AF_INET) {
+ inet_ntop(AF_INET, (void *) &((struct sockaddr_in *) &sp->local_addr)->sin_addr, ipl, sizeof(ipl));
+ mapped_v4_to_regular_v4(ipl);
+ inet_ntop(AF_INET, (void *) &((struct sockaddr_in *) &sp->remote_addr)->sin_addr, ipr, sizeof(ipr));
+ mapped_v4_to_regular_v4(ipr);
+ lport = ntohs(((struct sockaddr_in *) &sp->local_addr)->sin_port);
+ rport = ntohs(((struct sockaddr_in *) &sp->remote_addr)->sin_port);
+ } else {
+ inet_ntop(AF_INET6, (void *) &((struct sockaddr_in6 *) &sp->local_addr)->sin6_addr, ipl, sizeof(ipl));
+ mapped_v4_to_regular_v4(ipl);
+ inet_ntop(AF_INET6, (void *) &((struct sockaddr_in6 *) &sp->remote_addr)->sin6_addr, ipr, sizeof(ipr));
+ mapped_v4_to_regular_v4(ipr);
+ lport = ntohs(((struct sockaddr_in6 *) &sp->local_addr)->sin6_port);
+ rport = ntohs(((struct sockaddr_in6 *) &sp->remote_addr)->sin6_port);
+ }
+
+ if (sp->test->json_output)
+ cJSON_AddItemToArray(sp->test->json_connected, iperf_json_printf("socket: %d local_host: %s local_port: %d remote_host: %s remote_port: %d", (int64_t) sp->socket, ipl, (int64_t) lport, ipr, (int64_t) rport));
+ else
+ iperf_printf(sp->test, report_connected, sp->socket, ipl, lport, ipr, rport);
+}
+
+
+/**************************************************************************/
+
+struct iperf_test *
+iperf_new_test()
+{
+ struct iperf_test *test;
+ int rc;
+
+ test = (struct iperf_test *) malloc(sizeof(struct iperf_test));
+ if (!test) {
+ i_errno = IENEWTEST;
+ return NULL;
+ }
+ /* 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);
+ i_errno = IENEWTEST;
+ return NULL;
+ }
+ memset(test->settings, 0, sizeof(struct iperf_settings));
+
+ test->bitrate_limit_intervals_traffic_bytes = (iperf_size_t *) malloc(sizeof(iperf_size_t) * MAX_INTERVAL);
+ if (!test->bitrate_limit_intervals_traffic_bytes) {
+ free(test->settings);
+ free(test);
+ i_errno = IENEWTEST;
+ return NULL;
+ }
+ memset(test->bitrate_limit_intervals_traffic_bytes, 0, sizeof(sizeof(iperf_size_t) * MAX_INTERVAL));
+
+ /* By default all output goes to stdout */
+ test->outfile = stdout;
+
+ return test;
+}
+
+/**************************************************************************/
+
+struct protocol *
+protocol_new(void)
+{
+ struct protocol *proto;
+
+ proto = malloc(sizeof(struct protocol));
+ if(!proto) {
+ return NULL;
+ }
+ memset(proto, 0, sizeof(struct protocol));
+
+ return proto;
+}
+
+void
+protocol_free(struct protocol *proto)
+{
+ free(proto);
+}
+
+/**************************************************************************/
+int
+iperf_defaults(struct iperf_test *testp)
+{
+ struct protocol *tcp, *udp;
+#if defined(HAVE_SCTP_H)
+ struct protocol *sctp;
+#endif /* HAVE_SCTP_H */
+
+ testp->omit = OMIT;
+ testp->duration = DURATION;
+ testp->diskfile_name = (char*) 0;
+ testp->affinity = -1;
+ testp->server_affinity = -1;
+ TAILQ_INIT(&testp->xbind_addrs);
+#if defined(HAVE_CPUSET_SETAFFINITY)
+ CPU_ZERO(&testp->cpumask);
+#endif /* HAVE_CPUSET_SETAFFINITY */
+ testp->title = NULL;
+ testp->extra_data = NULL;
+ testp->congestion = NULL;
+ testp->congestion_used = NULL;
+ testp->remote_congestion_used = NULL;
+ testp->server_port = PORT;
+ testp->ctrl_sck = -1;
+ testp->listener = -1;
+ testp->prot_listener = -1;
+ testp->other_side_has_retransmits = 0;
+
+ testp->stats_callback = iperf_stats_callback;
+ testp->reporter_callback = iperf_reporter_callback;
+
+ testp->stats_interval = testp->reporter_interval = 1;
+ testp->num_streams = 1;
+
+ testp->settings->domain = AF_UNSPEC;
+ testp->settings->unit_format = 'a';
+ testp->settings->socket_bufsize = 0; /* use autotuning */
+ testp->settings->blksize = DEFAULT_TCP_BLKSIZE;
+ testp->settings->rate = 0;
+ testp->settings->bitrate_limit = 0;
+ testp->settings->bitrate_limit_interval = 5;
+ testp->settings->bitrate_limit_stats_per_interval = 0;
+ testp->settings->fqrate = 0;
+ testp->settings->pacing_timer = DEFAULT_PACING_TIMER;
+ testp->settings->burst = 0;
+ testp->settings->mss = 0;
+ testp->settings->bytes = 0;
+ testp->settings->blocks = 0;
+ testp->settings->connect_timeout = -1;
+ 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;
+
+ memset(testp->cookie, 0, COOKIE_SIZE);
+
+ testp->multisend = 10; /* arbitrary */
+
+ /* Set up protocol list */
+ SLIST_INIT(&testp->streams);
+ SLIST_INIT(&testp->protocols);
+
+ tcp = protocol_new();
+ if (!tcp)
+ return -1;
+
+ tcp->id = Ptcp;
+ tcp->name = "TCP";
+ tcp->accept = iperf_tcp_accept;
+ tcp->listen = iperf_tcp_listen;
+ tcp->connect = iperf_tcp_connect;
+ tcp->send = iperf_tcp_send;
+ tcp->recv = iperf_tcp_recv;
+ tcp->init = NULL;
+ SLIST_INSERT_HEAD(&testp->protocols, tcp, protocols);
+
+ udp = protocol_new();
+ if (!udp) {
+ protocol_free(tcp);
+ return -1;
+ }
+
+ udp->id = Pudp;
+ udp->name = "UDP";
+ udp->accept = iperf_udp_accept;
+ udp->listen = iperf_udp_listen;
+ udp->connect = iperf_udp_connect;
+ udp->send = iperf_udp_send;
+ udp->recv = iperf_udp_recv;
+ udp->init = iperf_udp_init;
+ SLIST_INSERT_AFTER(tcp, udp, protocols);
+
+ set_protocol(testp, Ptcp);
+
+#if defined(HAVE_SCTP_H)
+ sctp = protocol_new();
+ if (!sctp) {
+ protocol_free(tcp);
+ protocol_free(udp);
+ return -1;
+ }
+
+ sctp->id = Psctp;
+ sctp->name = "SCTP";
+ sctp->accept = iperf_sctp_accept;
+ sctp->listen = iperf_sctp_listen;
+ sctp->connect = iperf_sctp_connect;
+ sctp->send = iperf_sctp_send;
+ sctp->recv = iperf_sctp_recv;
+ sctp->init = iperf_sctp_init;
+
+ SLIST_INSERT_AFTER(udp, sctp, protocols);
+#endif /* HAVE_SCTP_H */
+
+ testp->on_new_stream = iperf_on_new_stream;
+ testp->on_test_start = iperf_on_test_start;
+ testp->on_connect = iperf_on_connect;
+ testp->on_test_finish = iperf_on_test_finish;
+
+ TAILQ_INIT(&testp->server_output_list);
+
+ return 0;
+}
+
+
+/**************************************************************************/
+void
+iperf_free_test(struct iperf_test *test)
+{
+ struct protocol *prot;
+ struct iperf_stream *sp;
+
+ /* Free streams */
+ while (!SLIST_EMPTY(&test->streams)) {
+ sp = SLIST_FIRST(&test->streams);
+ SLIST_REMOVE_HEAD(&test->streams, streams);
+ iperf_free_stream(sp);
+ }
+ if (test->server_hostname)
+ free(test->server_hostname);
+ if (test->tmp_template)
+ free(test->tmp_template);
+ if (test->bind_address)
+ free(test->bind_address);
+ if (test->bind_dev)
+ free(test->bind_dev);
+ if (!TAILQ_EMPTY(&test->xbind_addrs)) {
+ struct xbind_entry *xbe;
+
+ while (!TAILQ_EMPTY(&test->xbind_addrs)) {
+ xbe = TAILQ_FIRST(&test->xbind_addrs);
+ TAILQ_REMOVE(&test->xbind_addrs, xbe, link);
+ if (xbe->ai)
+ freeaddrinfo(xbe->ai);
+ free(xbe->name);
+ free(xbe);
+ }
+ }
+#if defined(HAVE_SSL)
+
+ if (test->server_rsa_private_key)
+ EVP_PKEY_free(test->server_rsa_private_key);
+ test->server_rsa_private_key = NULL;
+
+ free(test->settings->authtoken);
+ test->settings->authtoken = NULL;
+
+ free(test->settings->client_username);
+ test->settings->client_username = NULL;
+
+ free(test->settings->client_password);
+ test->settings->client_password = NULL;
+
+ if (test->settings->client_rsa_pubkey)
+ EVP_PKEY_free(test->settings->client_rsa_pubkey);
+ test->settings->client_rsa_pubkey = NULL;
+#endif /* HAVE_SSL */
+
+ if (test->settings)
+ free(test->settings);
+ if (test->title)
+ free(test->title);
+ if (test->extra_data)
+ free(test->extra_data);
+ if (test->congestion)
+ free(test->congestion);
+ if (test->congestion_used)
+ free(test->congestion_used);
+ if (test->remote_congestion_used)
+ free(test->remote_congestion_used);
+ if (test->timestamp_format)
+ free(test->timestamp_format);
+ if (test->omit_timer != NULL)
+ tmr_cancel(test->omit_timer);
+ if (test->timer != NULL)
+ tmr_cancel(test->timer);
+ if (test->stats_timer != NULL)
+ tmr_cancel(test->stats_timer);
+ if (test->reporter_timer != NULL)
+ tmr_cancel(test->reporter_timer);
+
+ /* Free protocol list */
+ while (!SLIST_EMPTY(&test->protocols)) {
+ prot = SLIST_FIRST(&test->protocols);
+ SLIST_REMOVE_HEAD(&test->protocols, protocols);
+ 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;
+ iperf_close_logfile(test);
+ }
+
+ if (test->server_output_text) {
+ free(test->server_output_text);
+ test->server_output_text = NULL;
+ }
+
+ if (test->json_output_string) {
+ free(test->json_output_string);
+ test->json_output_string = NULL;
+ }
+
+ /* Free output line buffers, if any (on the server only) */
+ struct iperf_textline *t;
+ while (!TAILQ_EMPTY(&test->server_output_list)) {
+ t = TAILQ_FIRST(&test->server_output_list);
+ TAILQ_REMOVE(&test->server_output_list, t, textlineentries);
+ free(t->line);
+ free(t);
+ }
+
+ /* sctp_bindx: do not free the arguments, only the resolver results */
+ if (!TAILQ_EMPTY(&test->xbind_addrs)) {
+ struct xbind_entry *xbe;
+
+ TAILQ_FOREACH(xbe, &test->xbind_addrs, link) {
+ if (xbe->ai) {
+ freeaddrinfo(xbe->ai);
+ xbe->ai = NULL;
+ }
+ }
+ }
+
+ /* Free interval's traffic array for average rate calculations */
+ if (test->bitrate_limit_intervals_traffic_bytes != NULL)
+ free(test->bitrate_limit_intervals_traffic_bytes);
+
+ /* XXX: Why are we setting these values to NULL? */
+ // test->streams = NULL;
+ test->stats_callback = NULL;
+ test->reporter_callback = NULL;
+ free(test);
+}
+
+
+void
+iperf_reset_test(struct iperf_test *test)
+{
+ struct iperf_stream *sp;
+ int i;
+
+ iperf_close_logfile(test);
+
+ /* Free streams */
+ while (!SLIST_EMPTY(&test->streams)) {
+ sp = SLIST_FIRST(&test->streams);
+ SLIST_REMOVE_HEAD(&test->streams, streams);
+ iperf_free_stream(sp);
+ }
+ if (test->omit_timer != NULL) {
+ tmr_cancel(test->omit_timer);
+ test->omit_timer = NULL;
+ }
+ if (test->timer != NULL) {
+ tmr_cancel(test->timer);
+ test->timer = NULL;
+ }
+ 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;
+ }
+ test->done = 0;
+
+ SLIST_INIT(&test->streams);
+
+ if (test->remote_congestion_used)
+ free(test->remote_congestion_used);
+ test->remote_congestion_used = NULL;
+ test->role = 's';
+ test->mode = RECEIVER;
+ test->sender_has_retransmits = 0;
+ set_protocol(test, Ptcp);
+ test->omit = OMIT;
+ test->duration = DURATION;
+ test->server_affinity = -1;
+#if defined(HAVE_CPUSET_SETAFFINITY)
+ CPU_ZERO(&test->cpumask);
+#endif /* HAVE_CPUSET_SETAFFINITY */
+ test->state = 0;
+
+ test->ctrl_sck = -1;
+ test->listener = -1;
+ test->prot_listener = -1;
+
+ test->bytes_sent = 0;
+ test->blocks_sent = 0;
+
+ test->bytes_received = 0;
+ test->blocks_received = 0;
+
+ test->other_side_has_retransmits = 0;
+
+ test->bitrate_limit_stats_count = 0;
+ test->bitrate_limit_last_interval_index = 0;
+ test->bitrate_limit_exceeded = 0;
+
+ for (i = 0; i < MAX_INTERVAL; i++)
+ test->bitrate_limit_intervals_traffic_bytes[i] = 0;
+
+ test->reverse = 0;
+ test->bidirectional = 0;
+ test->no_delay = 0;
+
+ FD_ZERO(&test->read_set);
+ FD_ZERO(&test->write_set);
+
+ test->num_streams = 1;
+ test->settings->socket_bufsize = 0;
+ test->settings->blksize = DEFAULT_TCP_BLKSIZE;
+ test->settings->rate = 0;
+ test->settings->burst = 0;
+ test->settings->mss = 0;
+ test->settings->tos = 0;
+ test->settings->dont_fragment = 0;
+ test->zerocopy = 0;
+
+#if defined(HAVE_SSL)
+ if (test->settings->authtoken) {
+ free(test->settings->authtoken);
+ test->settings->authtoken = NULL;
+ }
+ if (test->settings->client_username) {
+ free(test->settings->client_username);
+ test->settings->client_username = NULL;
+ }
+ if (test->settings->client_password) {
+ free(test->settings->client_password);
+ test->settings->client_password = NULL;
+ }
+ if (test->settings->client_rsa_pubkey) {
+ EVP_PKEY_free(test->settings->client_rsa_pubkey);
+ test->settings->client_rsa_pubkey = NULL;
+ }
+#endif /* HAVE_SSL */
+
+ memset(test->cookie, 0, COOKIE_SIZE);
+ test->multisend = 10; /* arbitrary */
+ test->udp_counters_64bit = 0;
+ if (test->title) {
+ free(test->title);
+ test->title = NULL;
+ }
+ if (test->extra_data) {
+ free(test->extra_data);
+ test->extra_data = NULL;
+ }
+
+ /* Free output line buffers, if any (on the server only) */
+ struct iperf_textline *t;
+ while (!TAILQ_EMPTY(&test->server_output_list)) {
+ t = TAILQ_FIRST(&test->server_output_list);
+ TAILQ_REMOVE(&test->server_output_list, t, textlineentries);
+ free(t->line);
+ free(t);
+ }
+}
+
+
+/* Reset all of a test's stats back to zero. Called when the omitting
+** period is over.
+*/
+void
+iperf_reset_stats(struct iperf_test *test)
+{
+ struct iperf_time now;
+ struct iperf_stream *sp;
+ struct iperf_stream_result *rp;
+
+ test->bytes_sent = 0;
+ test->blocks_sent = 0;
+ iperf_time_now(&now);
+ SLIST_FOREACH(sp, &test->streams, streams) {
+ sp->omitted_packet_count = sp->packet_count;
+ sp->omitted_cnt_error = sp->cnt_error;
+ sp->omitted_outoforder_packets = sp->outoforder_packets;
+ sp->jitter = 0;
+ rp = sp->result;
+ rp->bytes_sent_omit = rp->bytes_sent;
+ rp->bytes_received = 0;
+ rp->bytes_sent_this_interval = rp->bytes_received_this_interval = 0;
+ if (test->sender_has_retransmits == 1) {
+ struct iperf_interval_results ir; /* temporary results structure */
+ save_tcpinfo(sp, &ir);
+ rp->stream_prev_total_retrans = get_total_retransmits(&ir);
+ }
+ rp->stream_retrans = 0;
+ rp->start_time = now;
+ }
+}
+
+
+/**************************************************************************/
+
+/**
+ * Gather statistics during a test.
+ * This function works for both the client and server side.
+ */
+void
+iperf_stats_callback(struct iperf_test *test)
+{
+ struct iperf_stream *sp;
+ struct iperf_stream_result *rp = NULL;
+ struct iperf_interval_results *irp, temp;
+ struct iperf_time temp_time;
+ iperf_size_t total_interval_bytes_transferred = 0;
+
+ temp.omitted = test->omitting;
+ SLIST_FOREACH(sp, &test->streams, streams) {
+ rp = sp->result;
+ temp.bytes_transferred = sp->sender ? rp->bytes_sent_this_interval : rp->bytes_received_this_interval;
+
+ // Total bytes transferred this interval
+ total_interval_bytes_transferred += rp->bytes_sent_this_interval + rp->bytes_received_this_interval;
+
+ irp = TAILQ_LAST(&rp->interval_results, irlisthead);
+ /* result->end_time contains timestamp of previous interval */
+ if ( irp != NULL ) /* not the 1st interval */
+ memcpy(&temp.interval_start_time, &rp->end_time, sizeof(struct iperf_time));
+ else /* or use timestamp from beginning */
+ memcpy(&temp.interval_start_time, &rp->start_time, sizeof(struct iperf_time));
+ /* now save time of end of this interval */
+ iperf_time_now(&rp->end_time);
+ memcpy(&temp.interval_end_time, &rp->end_time, sizeof(struct iperf_time));
+ iperf_time_diff(&temp.interval_start_time, &temp.interval_end_time, &temp_time);
+ temp.interval_duration = iperf_time_in_secs(&temp_time);
+ if (test->protocol->id == Ptcp) {
+ if ( has_tcpinfo()) {
+ save_tcpinfo(sp, &temp);
+ if (test->sender_has_retransmits == 1) {
+ long total_retrans = get_total_retransmits(&temp);
+ temp.interval_retrans = total_retrans - rp->stream_prev_total_retrans;
+ rp->stream_retrans += temp.interval_retrans;
+ rp->stream_prev_total_retrans = total_retrans;
+
+ temp.snd_cwnd = get_snd_cwnd(&temp);
+ if (temp.snd_cwnd > rp->stream_max_snd_cwnd) {
+ rp->stream_max_snd_cwnd = temp.snd_cwnd;
+ }
+
+ temp.snd_wnd = get_snd_wnd(&temp);
+ if (temp.snd_wnd > rp->stream_max_snd_wnd) {
+ rp->stream_max_snd_wnd = temp.snd_wnd;
+ }
+
+ temp.rtt = get_rtt(&temp);
+ 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++;
+
+ temp.rttvar = get_rttvar(&temp);
+ temp.pmtu = get_pmtu(&temp);
+ }
+ }
+ } else {
+ if (irp == NULL) {
+ temp.interval_packet_count = sp->packet_count;
+ temp.interval_outoforder_packets = sp->outoforder_packets;
+ temp.interval_cnt_error = sp->cnt_error;
+ } else {
+ temp.interval_packet_count = sp->packet_count - irp->packet_count;
+ temp.interval_outoforder_packets = sp->outoforder_packets - irp->outoforder_packets;
+ temp.interval_cnt_error = sp->cnt_error - irp->cnt_error;
+ }
+ temp.packet_count = sp->packet_count;
+ temp.jitter = sp->jitter;
+ temp.outoforder_packets = sp->outoforder_packets;
+ temp.cnt_error = sp->cnt_error;
+ }
+ add_to_interval_list(rp, &temp);
+ rp->bytes_sent_this_interval = rp->bytes_received_this_interval = 0;
+ }
+
+ /* Verify that total server's throughput is not above specified limit */
+ if (test->role == 's') {
+ iperf_check_total_rate(test, total_interval_bytes_transferred);
+ }
+}
+
+/**
+ * Print intermediate results during a test (interval report).
+ * Uses print_interval_results to print the results for each stream,
+ * then prints an interval summary for all streams in this
+ * interval.
+ */
+static void
+iperf_print_intermediate(struct iperf_test *test)
+{
+ struct iperf_stream *sp = NULL;
+ struct iperf_interval_results *irp;
+ struct iperf_time temp_time;
+ cJSON *json_interval;
+ cJSON *json_interval_streams;
+
+ int lower_mode, upper_mode;
+ int current_mode;
+
+ /*
+ * Due to timing oddities, there can be cases, especially on the
+ * server side, where at the end of a test there is a fairly short
+ * interval with no data transferred. This could caused by
+ * the control and data flows sharing the same path in the network,
+ * and having the control messages for stopping the test being
+ * queued behind the data packets.
+ *
+ * We'd like to try to omit that last interval when it happens, to
+ * avoid cluttering data and output with useless stuff.
+ * So we're going to try to ignore very short intervals (less than
+ * 10% of the interval time) that have no data.
+ */
+ int interval_ok = 0;
+ SLIST_FOREACH(sp, &test->streams, streams) {
+ irp = TAILQ_LAST(&sp->result->interval_results, irlisthead);
+ if (irp) {
+ iperf_time_diff(&irp->interval_start_time, &irp->interval_end_time, &temp_time);
+ double interval_len = iperf_time_in_secs(&temp_time);
+ if (test->debug) {
+ printf("interval_len %f bytes_transferred %" PRIu64 "\n", interval_len, irp->bytes_transferred);
+ }
+
+ /*
+ * If the interval is at least 10% the normal interval
+ * length, or if there were actual bytes transferred,
+ * then we want to keep this interval.
+ */
+ if (interval_len >= test->stats_interval * 0.10 ||
+ irp->bytes_transferred > 0) {
+ interval_ok = 1;
+ if (test->debug) {
+ printf("interval forces keep\n");
+ }
+ }
+ }
+ }
+ if (!interval_ok) {
+ if (test->debug) {
+ printf("ignoring short interval with no data\n");
+ }
+ return;
+ }
+
+ if (test->json_output) {
+ json_interval = cJSON_CreateObject();
+ if (json_interval == NULL)
+ return;
+ cJSON_AddItemToArray(test->json_intervals, json_interval);
+ json_interval_streams = cJSON_CreateArray();
+ if (json_interval_streams == NULL)
+ return;
+ cJSON_AddItemToObject(json_interval, "streams", json_interval_streams);
+ } else {
+ json_interval = NULL;
+ json_interval_streams = NULL;
+ }
+
+ /*
+ * We must to sum streams separately.
+ * For bidirectional mode we must to display
+ * information about sender and receiver streams.
+ * For client side we must handle sender streams
+ * firstly and receiver streams for server side.
+ * The following design allows us to do this.
+ */
+
+ if (test->mode == BIDIRECTIONAL) {
+ if (test->role == 'c') {
+ lower_mode = -1;
+ upper_mode = 0;
+ } else {
+ lower_mode = 0;
+ upper_mode = 1;
+ }
+ } else {
+ lower_mode = test->mode;
+ upper_mode = lower_mode;
+ }
+
+
+ for (current_mode = lower_mode; current_mode <= upper_mode; ++current_mode) {
+ char ubuf[UNIT_LEN];
+ char nbuf[UNIT_LEN];
+ char mbuf[UNIT_LEN];
+ char zbuf[] = " ";
+
+ iperf_size_t bytes = 0;
+ double bandwidth;
+ int retransmits = 0;
+ double start_time, end_time;
+
+ int64_t total_packets = 0, lost_packets = 0;
+ double avg_jitter = 0.0, lost_percent;
+ int stream_must_be_sender = current_mode * current_mode;
+
+ char *sum_name;
+
+ /* 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");
+ } else {
+ mbuf[0] = '\0';
+ zbuf[0] = '\0';
+ }
+
+ SLIST_FOREACH(sp, &test->streams, streams) {
+ if (sp->sender == stream_must_be_sender) {
+ print_interval_results(test, sp, json_interval_streams);
+ /* sum up all streams */
+ irp = TAILQ_LAST(&sp->result->interval_results, irlisthead);
+ if (irp == NULL) {
+ iperf_err(test,
+ "iperf_print_intermediate error: interval_results is NULL");
+ return;
+ }
+ bytes += irp->bytes_transferred;
+ if (test->protocol->id == Ptcp) {
+ if (test->sender_has_retransmits == 1) {
+ retransmits += irp->interval_retrans;
+ }
+ } else {
+ total_packets += irp->interval_packet_count;
+ lost_packets += irp->interval_cnt_error;
+ avg_jitter += irp->jitter;
+ }
+ }
+ }
+
+ /* next build string with sum of all streams */
+ if (test->num_streams > 1 || test->json_output) {
+ /*
+ * With BIDIR give a different JSON object name to the one sent/receive sums.
+ * The different name is given to the data sent from the server, which is
+ * the "reverse" channel. This makes sure that the name reported on the server
+ * and client are compatible, and the names are the same as with non-bidir,
+ * except for when reverse is used.
+ */
+ sum_name = "sum";
+ if (test->mode == BIDIRECTIONAL) {
+ if ((test->role == 'c' && !stream_must_be_sender) ||
+ (test->role != 'c' && stream_must_be_sender))
+ {
+ sum_name = "sum_bidir_reverse";
+ }
+ }
+
+ sp = SLIST_FIRST(&test->streams); /* reset back to 1st stream */
+ /* Only do this of course if there was a first stream */
+ if (sp) {
+ irp = TAILQ_LAST(&sp->result->interval_results, irlisthead); /* use 1st stream for timing info */
+
+ unit_snprintf(ubuf, UNIT_LEN, (double) bytes, 'A');
+ bandwidth = (double) bytes / (double) irp->interval_duration;
+ unit_snprintf(nbuf, UNIT_LEN, bandwidth, test->settings->unit_format);
+
+ iperf_time_diff(&sp->result->start_time,&irp->interval_start_time, &temp_time);
+ start_time = iperf_time_in_secs(&temp_time);
+ iperf_time_diff(&sp->result->start_time,&irp->interval_end_time, &temp_time);
+ end_time = iperf_time_in_secs(&temp_time);
+ if (test->protocol->id == Ptcp || test->protocol->id == Psctp) {
+ 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? */
+ 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 {
+ /* Interval sum, TCP without 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 omitted: %b sender: %b", (double) start_time, (double) end_time, (double) irp->interval_duration, (int64_t) bytes, bandwidth * 8, test->omitting, stream_must_be_sender));
+ else
+ iperf_printf(test, report_sum_bw_format, mbuf, start_time, end_time, ubuf, nbuf, test->omitting?report_omitted:"");
+ }
+ } else {
+ /* Interval sum, UDP. */
+ if (stream_must_be_sender) {
+ 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 packets: %d omitted: %b sender: %b", (double) start_time, (double) end_time, (double) irp->interval_duration, (int64_t) bytes, bandwidth * 8, (int64_t) total_packets, test->omitting, stream_must_be_sender));
+ else
+ iperf_printf(test, report_sum_bw_udp_sender_format, mbuf, start_time, end_time, ubuf, nbuf, zbuf, total_packets, test->omitting?report_omitted:"");
+ } else {
+ avg_jitter /= test->num_streams;
+ if (total_packets > 0) {
+ lost_percent = 100.0 * lost_packets / total_packets;
+ }
+ else {
+ lost_percent = 0.0;
+ }
+ 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 jitter_ms: %f lost_packets: %d packets: %d lost_percent: %f omitted: %b sender: %b", (double) start_time, (double) end_time, (double) irp->interval_duration, (int64_t) bytes, bandwidth * 8, (double) avg_jitter * 1000.0, (int64_t) lost_packets, (int64_t) total_packets, (double) lost_percent, test->omitting, stream_must_be_sender));
+ else
+ iperf_printf(test, report_sum_bw_udp_format, mbuf, start_time, end_time, ubuf, nbuf, avg_jitter * 1000.0, lost_packets, total_packets, lost_percent, test->omitting?report_omitted:"");
+ }
+ }
+ }
+ }
+ }
+}
+
+/**
+ * Print overall summary statistics at the end of a test.
+ */
+static void
+iperf_print_results(struct iperf_test *test)
+{
+
+ cJSON *json_summary_streams = NULL;
+
+ int lower_mode, upper_mode;
+ int current_mode;
+
+ char *sum_sent_name, *sum_received_name, *sum_name;
+
+ int tmp_sender_has_retransmits = test->sender_has_retransmits;
+
+ /* print final summary for all intervals */
+
+ if (test->json_output) {
+ json_summary_streams = cJSON_CreateArray();
+ if (json_summary_streams == NULL)
+ return;
+ cJSON_AddItemToObject(test->json_end, "streams", json_summary_streams);
+ } else {
+ iperf_printf(test, "%s", report_bw_separator);
+ if (test->verbose)
+ iperf_printf(test, "%s", report_summary);
+ if (test->protocol->id == Ptcp || test->protocol->id == Psctp) {
+ if (test->sender_has_retransmits || test->other_side_has_retransmits) {
+ if (test->bidirectional)
+ iperf_printf(test, "%s", report_bw_retrans_header_bidir);
+ else
+ iperf_printf(test, "%s", report_bw_retrans_header);
+ }
+ else {
+ if (test->bidirectional)
+ iperf_printf(test, "%s", report_bw_header_bidir);
+ else
+ iperf_printf(test, "%s", report_bw_header);
+ }
+ } else {
+ if (test->bidirectional)
+ iperf_printf(test, "%s", report_bw_udp_header_bidir);
+ else
+ iperf_printf(test, "%s", report_bw_udp_header);
+ }
+ }
+
+ /*
+ * We must to sum streams separately.
+ * For bidirectional mode we must to display
+ * information about sender and receiver streams.
+ * For client side we must handle sender streams
+ * firstly and receiver streams for server side.
+ * The following design allows us to do this.
+ */
+
+ if (test->mode == BIDIRECTIONAL) {
+ if (test->role == 'c') {
+ lower_mode = -1;
+ upper_mode = 0;
+ } else {
+ lower_mode = 0;
+ upper_mode = 1;
+ }
+ } else {
+ lower_mode = test->mode;
+ upper_mode = lower_mode;
+ }
+
+
+ for (current_mode = lower_mode; current_mode <= upper_mode; ++current_mode) {
+ cJSON *json_summary_stream = NULL;
+ 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;
+ char sbuf[UNIT_LEN];
+ struct iperf_stream *sp = NULL;
+ iperf_size_t bytes_sent, total_sent = 0;
+ 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;
+ double bandwidth;
+
+ char mbuf[UNIT_LEN];
+ int stream_must_be_sender = current_mode * current_mode;
+
+
+ /* 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");
+ } else {
+ mbuf[0] = '\0';
+ }
+
+ /* Get sender_has_retransmits for each sender side (client and server) */
+ if (test->mode == BIDIRECTIONAL && stream_must_be_sender)
+ test->sender_has_retransmits = tmp_sender_has_retransmits;
+ else if (test->mode == BIDIRECTIONAL && !stream_must_be_sender)
+ test->sender_has_retransmits = test->other_side_has_retransmits;
+
+ start_time = 0.;
+ sp = SLIST_FIRST(&test->streams);
+
+ /*
+ * If there is at least one stream, then figure out the length of time
+ * we were running the tests and print out some statistics about
+ * the streams. It's possible to not have any streams at all
+ * if the client got interrupted before it got to do anything.
+ *
+ * Also note that we try to keep separate values for the sender
+ * and receiver ending times. Earlier iperf (3.1 and earlier)
+ * servers didn't send that to the clients, so in this case we fall
+ * back to using the client's ending timestamp. The fallback is
+ * basically emulating what iperf 3.1 did.
+ */
+
+ if (sp) {
+ 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) {
+ sp->result->receiver_time = sp->result->sender_time;
+ }
+ }
+ else {
+ sp->result->receiver_time = end_time;
+ if (sp->result->sender_time == 0.0) {
+ sp->result->sender_time = sp->result->receiver_time;
+ }
+ }
+ sender_time = sp->result->sender_time;
+ receiver_time = sp->result->receiver_time;
+ SLIST_FOREACH(sp, &test->streams, streams) {
+ if (sp->sender == stream_must_be_sender) {
+ if (test->json_output) {
+ json_summary_stream = cJSON_CreateObject();
+ if (json_summary_stream == NULL)
+ return;
+ cJSON_AddItemToArray(json_summary_streams, json_summary_stream);
+ }
+
+ bytes_sent = sp->result->bytes_sent - sp->result->bytes_sent_omit;
+ bytes_received = sp->result->bytes_received;
+ total_sent += bytes_sent;
+ total_received += bytes_received;
+
+ 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) {
+ if (test->sender_has_retransmits) {
+ total_retransmits += sp->result->stream_retrans;
+ }
+ } else {
+ /*
+ * Running total of the total number of packets. Use the sender packet count if we
+ * have it, otherwise use the 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 - 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;
+ }
+
+ unit_snprintf(ubuf, UNIT_LEN, (double) bytes_sent, 'A');
+ if (sender_time > 0.0) {
+ bandwidth = (double) bytes_sent / (double) sender_time;
+ }
+ else {
+ bandwidth = 0.0;
+ }
+ unit_snprintf(nbuf, UNIT_LEN, bandwidth, test->settings->unit_format);
+ if (test->protocol->id == Ptcp || test->protocol->id == Psctp) {
+ if (test->sender_has_retransmits) {
+ /* Sender summary, TCP and SCTP with retransmits. */
+ if (test->json_output)
+ 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 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));
+ else
+ if (test->role == 's' && !sp->sender) {
+ if (test->verbose)
+ iperf_printf(test, report_sender_not_available_format, sp->socket);
+ }
+ else {
+ iperf_printf(test, report_bw_retrans_format, sp->socket, mbuf, start_time, sender_time, ubuf, nbuf, sp->result->stream_retrans, report_sender);
+ }
+ } else {
+ /* Sender summary, TCP and SCTP without retransmits. */
+ if (test->json_output)
+ 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)
+ iperf_printf(test, report_sender_not_available_format, sp->socket);
+ }
+ else {
+ iperf_printf(test, report_bw_format, sp->socket, mbuf, start_time, sender_time, ubuf, nbuf, report_sender);
+ }
+ }
+ } else {
+ /* Sender summary, UDP. */
+ 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 historical reasons, we only emit one JSON
+ * object for the UDP summary, and it contains
+ * information for both the sender and receiver
+ * side.
+ *
+ * The JSON format as currently defined only includes one
+ * value for the number of packets. We usually want that
+ * to be the sender's value (how many packets were sent
+ * by the sender). However this value might not be
+ * available on the receiver in certain circumstances
+ * specifically on the server side for a normal test or
+ * the client side for a reverse-mode test. If this
+ * is the case, then use the receiver's count of packets
+ * instead.
+ */
+ 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 {
+ /*
+ * Due to ordering of messages on the control channel,
+ * the server cannot report on client-side summary
+ * statistics. If we're the server, omit one set of
+ * summary statistics to avoid giving meaningless
+ * results.
+ */
+ if (test->role == 's' && !sp->sender) {
+ if (test->verbose)
+ 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, (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));
+ }
+ }
+
+ if (sp->diskfile_fd >= 0) {
+ if (fstat(sp->diskfile_fd, &sb) == 0) {
+ /* In the odd case that it's a zero-sized file, say it was all transferred. */
+ int percent_sent = 100, percent_received = 100;
+ if (sb.st_size > 0) {
+ percent_sent = (int) ( ( (double) bytes_sent / (double) sb.st_size ) * 100.0 );
+ percent_received = (int) ( ( (double) bytes_received / (double) sb.st_size ) * 100.0 );
+ }
+ unit_snprintf(sbuf, UNIT_LEN, (double) sb.st_size, 'A');
+ if (test->json_output)
+ cJSON_AddItemToObject(json_summary_stream, "diskfile", iperf_json_printf("sent: %d received: %d size: %d percent_sent: %d percent_received: %d filename: %s", (int64_t) bytes_sent, (int64_t) bytes_received, (int64_t) sb.st_size, (int64_t) percent_sent, (int64_t) percent_received, test->diskfile_name));
+ else
+ if (stream_must_be_sender) {
+ iperf_printf(test, report_diskfile, ubuf, sbuf, percent_sent, test->diskfile_name);
+ }
+ else {
+ unit_snprintf(ubuf, UNIT_LEN, (double) bytes_received, 'A');
+ iperf_printf(test, report_diskfile, ubuf, sbuf, percent_received, test->diskfile_name);
+ }
+ }
+ }
+
+ unit_snprintf(ubuf, UNIT_LEN, (double) bytes_received, 'A');
+ if (receiver_time > 0) {
+ bandwidth = (double) bytes_received / (double) receiver_time;
+ }
+ else {
+ bandwidth = 0.0;
+ }
+ unit_snprintf(nbuf, UNIT_LEN, bandwidth, test->settings->unit_format);
+ if (test->protocol->id == Ptcp || test->protocol->id == Psctp) {
+ /* Receiver summary, TCP and SCTP */
+ if (test->json_output)
+ 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)
+ iperf_printf(test, report_receiver_not_available_format, sp->socket);
+ }
+ else {
+ iperf_printf(test, report_bw_format, sp->socket, mbuf, start_time, receiver_time, ubuf, nbuf, report_receiver);
+ }
+ }
+ else {
+ /*
+ * Receiver summary, UDP. Note that JSON was emitted with
+ * the sender summary, so we only deal with human-readable
+ * data here.
+ */
+ if (! test->json_output) {
+ 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;
+ }
+
+ if (test->role == 's' && sp->sender) {
+ if (test->verbose)
+ iperf_printf(test, report_receiver_not_available_format, sp->socket);
+ }
+ 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);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (test->num_streams > 1 || test->json_output) {
+ /*
+ * With BIDIR give a different JSON object name to the one sent/receive sums.
+ * The different name is given to the data sent from the server, which is
+ * the "reverse" channel. This makes sure that the name reported on the server
+ * and client are compatible, and the names are the same as with non-bidir,
+ * except for when reverse is used.
+ */
+ sum_name = "sum";
+ sum_sent_name = "sum_sent";
+ sum_received_name = "sum_received";
+ if (test->mode == BIDIRECTIONAL) {
+ if ((test->role == 'c' && !stream_must_be_sender) ||
+ (test->role != 'c' && stream_must_be_sender))
+ {
+ sum_name = "sum_bidir_reverse";
+ sum_sent_name = "sum_sent_bidir_reverse";
+ sum_received_name = "sum_received_bidir_reverse";
+ }
+
+ }
+
+ unit_snprintf(ubuf, UNIT_LEN, (double) total_sent, 'A');
+ /* If no tests were run, arbitrarily set bandwidth to 0. */
+ if (sender_time > 0.0) {
+ bandwidth = (double) total_sent / (double) sender_time;
+ }
+ else {
+ bandwidth = 0.0;
+ }
+ unit_snprintf(nbuf, UNIT_LEN, bandwidth, test->settings->unit_format);
+ if (test->protocol->id == Ptcp || test->protocol->id == Psctp) {
+ if (test->sender_has_retransmits) {
+ /* Summary sum, TCP with retransmits. */
+ if (test->json_output)
+ cJSON_AddItemToObject(test->json_end, sum_sent_name, iperf_json_printf("start: %f end: %f seconds: %f bytes: %d bits_per_second: %f retransmits: %d sender: %b", (double) start_time, (double) sender_time, (double) sender_time, (int64_t) total_sent, bandwidth * 8, (int64_t) total_retransmits, stream_must_be_sender));
+ else
+ if (test->role == 's' && !stream_must_be_sender) {
+ if (test->verbose)
+ iperf_printf(test, report_sender_not_available_summary_format, "SUM");
+ }
+ else {
+ iperf_printf(test, report_sum_bw_retrans_format, mbuf, start_time, sender_time, ubuf, nbuf, total_retransmits, report_sender);
+ }
+ } else {
+ /* Summary sum, TCP without retransmits. */
+ if (test->json_output)
+ cJSON_AddItemToObject(test->json_end, sum_sent_name, iperf_json_printf("start: %f end: %f seconds: %f bytes: %d bits_per_second: %f sender: %b", (double) start_time, (double) sender_time, (double) sender_time, (int64_t) total_sent, bandwidth * 8, stream_must_be_sender));
+ else
+ if (test->role == 's' && !stream_must_be_sender) {
+ if (test->verbose)
+ iperf_printf(test, report_sender_not_available_summary_format, "SUM");
+ }
+ else {
+ iperf_printf(test, report_sum_bw_format, mbuf, start_time, sender_time, ubuf, nbuf, report_sender);
+ }
+ }
+ unit_snprintf(ubuf, UNIT_LEN, (double) total_received, 'A');
+ /* If no tests were run, set received bandwidth to 0 */
+ if (receiver_time > 0.0) {
+ bandwidth = (double) total_received / (double) receiver_time;
+ }
+ else {
+ bandwidth = 0.0;
+ }
+ unit_snprintf(nbuf, UNIT_LEN, bandwidth, test->settings->unit_format);
+ if (test->json_output)
+ cJSON_AddItemToObject(test->json_end, sum_received_name, iperf_json_printf("start: %f end: %f seconds: %f bytes: %d bits_per_second: %f sender: %b", (double) start_time, (double) receiver_time, (double) receiver_time, (int64_t) total_received, bandwidth * 8, stream_must_be_sender));
+ else
+ if (test->role == 's' && stream_must_be_sender) {
+ if (test->verbose)
+ iperf_printf(test, report_receiver_not_available_summary_format, "SUM");
+ }
+ else {
+ iperf_printf(test, report_sum_bw_format, mbuf, start_time, receiver_time, ubuf, nbuf, report_receiver);
+ }
+ } else {
+ /* Summary sum, UDP. */
+ avg_jitter /= test->num_streams;
+ /* If no packets were sent, arbitrarily set loss percentage to 0. */
+ if (total_packets > 0) {
+ lost_percent = 100.0 * lost_packets / total_packets;
+ }
+ else {
+ lost_percent = 0.0;
+ }
+ if (test->json_output) {
+ /*
+ * Original, summary structure. Using this
+ * structure is not recommended due to
+ * 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));
+ /*
+ * 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));
+ } else {
+ /*
+ * On the client we have both sender and receiver overall summary
+ * stats. On the server we have only the side that was on the
+ * server. Output whatever we have.
+ */
+ 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, (int64_t) 0, sender_total_packets, 0.0, report_sender);
+ }
+ if (! (test->role == 's' && stream_must_be_sender) ) {
+
+ unit_snprintf(ubuf, UNIT_LEN, (double) total_received, 'A');
+ /* Compute received bandwidth. */
+ if (end_time > 0.0) {
+ bandwidth = (double) total_received / (double) receiver_time;
+ }
+ else {
+ 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, report_receiver);
+ }
+ }
+ }
+ }
+
+ if (test->json_output && current_mode == upper_mode) {
+ cJSON_AddItemToObject(test->json_end, "cpu_utilization_percent", iperf_json_printf("host_total: %f host_user: %f host_system: %f remote_total: %f remote_user: %f remote_system: %f", (double) test->cpu_util[0], (double) test->cpu_util[1], (double) test->cpu_util[2], (double) test->remote_cpu_util[0], (double) test->remote_cpu_util[1], (double) test->remote_cpu_util[2]));
+ if (test->protocol->id == Ptcp) {
+ char *snd_congestion = NULL, *rcv_congestion = NULL;
+ if (stream_must_be_sender) {
+ snd_congestion = test->congestion_used;
+ rcv_congestion = test->remote_congestion_used;
+ }
+ else {
+ snd_congestion = test->remote_congestion_used;
+ rcv_congestion = test->congestion_used;
+ }
+ if (snd_congestion) {
+ cJSON_AddStringToObject(test->json_end, "sender_tcp_congestion", snd_congestion);
+ }
+ if (rcv_congestion) {
+ cJSON_AddStringToObject(test->json_end, "receiver_tcp_congestion", rcv_congestion);
+ }
+ }
+ }
+ else {
+ if (test->verbose) {
+ if (stream_must_be_sender) {
+ if (test->bidirectional) {
+ iperf_printf(test, report_cpu, report_local, stream_must_be_sender?report_sender:report_receiver, test->cpu_util[0], test->cpu_util[1], test->cpu_util[2], report_remote, stream_must_be_sender?report_receiver:report_sender, test->remote_cpu_util[0], test->remote_cpu_util[1], test->remote_cpu_util[2]);
+ iperf_printf(test, report_cpu, report_local, !stream_must_be_sender?report_sender:report_receiver, test->cpu_util[0], test->cpu_util[1], test->cpu_util[2], report_remote, !stream_must_be_sender?report_receiver:report_sender, test->remote_cpu_util[0], test->remote_cpu_util[1], test->remote_cpu_util[2]);
+ } else
+ iperf_printf(test, report_cpu, report_local, stream_must_be_sender?report_sender:report_receiver, test->cpu_util[0], test->cpu_util[1], test->cpu_util[2], report_remote, stream_must_be_sender?report_receiver:report_sender, test->remote_cpu_util[0], test->remote_cpu_util[1], test->remote_cpu_util[2]);
+ }
+ if (test->protocol->id == Ptcp) {
+ char *snd_congestion = NULL, *rcv_congestion = NULL;
+ if (stream_must_be_sender) {
+ snd_congestion = test->congestion_used;
+ rcv_congestion = test->remote_congestion_used;
+ }
+ else {
+ snd_congestion = test->remote_congestion_used;
+ rcv_congestion = test->congestion_used;
+ }
+ if (snd_congestion) {
+ iperf_printf(test, "snd_tcp_congestion %s\n", snd_congestion);
+ }
+ if (rcv_congestion) {
+ iperf_printf(test, "rcv_tcp_congestion %s\n", rcv_congestion);
+ }
+ }
+ }
+
+ /* Print server output if we're on the client and it was requested/provided */
+ if (test->role == 'c' && iperf_get_test_get_server_output(test) && !test->json_output) {
+ if (test->json_server_output) {
+ char *str = cJSON_Print(test->json_server_output);
+ iperf_printf(test, "\nServer JSON output:\n%s\n", str);
+ cJSON_free(str);
+ cJSON_Delete(test->json_server_output);
+ test->json_server_output = NULL;
+ }
+ if (test->server_output_text) {
+ iperf_printf(test, "\nServer output:\n%s\n", test->server_output_text);
+ test->server_output_text = NULL;
+ }
+ }
+ }
+ }
+
+ /* Set real sender_has_retransmits for current side */
+ if (test->mode == BIDIRECTIONAL)
+ test->sender_has_retransmits = tmp_sender_has_retransmits;
+}
+
+/**************************************************************************/
+
+/**
+ * Main report-printing callback.
+ * Prints results either during a test (interval report only) or
+ * after the entire test has been run (last interval report plus
+ * overall summary).
+ */
+void
+iperf_reporter_callback(struct iperf_test *test)
+{
+ switch (test->state) {
+ case TEST_RUNNING:
+ case STREAM_RUNNING:
+ /* print interval results for each stream */
+ iperf_print_intermediate(test);
+ break;
+ case TEST_END:
+ case DISPLAY_RESULTS:
+ iperf_print_intermediate(test);
+ iperf_print_results(test);
+ break;
+ }
+
+}
+
+/**
+ * Print the interval results for one stream.
+ * This function needs to know about the overall test so it can determine the
+ * context for printing headers, separators, etc.
+ */
+static void
+print_interval_results(struct iperf_test *test, struct iperf_stream *sp, cJSON *json_interval_streams)
+{
+ char ubuf[UNIT_LEN];
+ char nbuf[UNIT_LEN];
+ char cbuf[UNIT_LEN];
+ char mbuf[UNIT_LEN];
+ char zbuf[] = " ";
+ double st = 0., et = 0.;
+ struct iperf_time temp_time;
+ struct iperf_interval_results *irp = NULL;
+ double bandwidth, lost_percent;
+
+ if (test->mode == BIDIRECTIONAL) {
+ sprintf(mbuf, "[%s-%s]", sp->sender?"TX":"RX", test->role == 'c'?"C":"S");
+ } else {
+ mbuf[0] = '\0';
+ zbuf[0] = '\0';
+ }
+
+ irp = TAILQ_LAST(&sp->result->interval_results, irlisthead); /* get last entry in linked list */
+ if (irp == NULL) {
+ iperf_err(test, "print_interval_results error: interval_results is NULL");
+ return;
+ }
+ if (!test->json_output) {
+ /* First stream? */
+ if (sp == SLIST_FIRST(&test->streams)) {
+ /* It it's the first interval, print the header;
+ ** else if there's more than one stream, print the separator;
+ ** else nothing.
+ */
+ if (iperf_time_compare(&sp->result->start_time, &irp->interval_start_time) == 0) {
+ if (test->protocol->id == Ptcp || test->protocol->id == Psctp) {
+ if (test->sender_has_retransmits == 1) {
+ if (test->bidirectional)
+ iperf_printf(test, "%s", report_bw_retrans_cwnd_header_bidir);
+ else
+ iperf_printf(test, "%s", report_bw_retrans_cwnd_header);
+ }
+ else {
+ if (test->bidirectional)
+ iperf_printf(test, "%s", report_bw_header_bidir);
+ else
+ iperf_printf(test, "%s", report_bw_header);
+ }
+ } else {
+ if (test->mode == SENDER) {
+ iperf_printf(test, "%s", report_bw_udp_sender_header);
+ } else if (test->mode == RECEIVER){
+ iperf_printf(test, "%s", report_bw_udp_header);
+ } else {
+ /* BIDIRECTIONAL */
+ iperf_printf(test, "%s", report_bw_udp_header_bidir);
+ }
+ }
+ } else if (test->num_streams > 1)
+ iperf_printf(test, "%s", report_bw_separator);
+ }
+ }
+
+ unit_snprintf(ubuf, UNIT_LEN, (double) (irp->bytes_transferred), 'A');
+ if (irp->interval_duration > 0.0) {
+ bandwidth = (double) irp->bytes_transferred / (double) irp->interval_duration;
+ }
+ else {
+ bandwidth = 0.0;
+ }
+ unit_snprintf(nbuf, UNIT_LEN, bandwidth, test->settings->unit_format);
+
+ iperf_time_diff(&sp->result->start_time, &irp->interval_start_time, &temp_time);
+ st = iperf_time_in_secs(&temp_time);
+ iperf_time_diff(&sp->result->start_time, &irp->interval_end_time, &temp_time);
+ et = iperf_time_in_secs(&temp_time);
+
+ if (test->protocol->id == Ptcp || test->protocol->id == Psctp) {
+ if (test->sender_has_retransmits == 1 && 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));
+ 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:"");
+ }
+ } else {
+ /* Interval, TCP without 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 omitted: %b sender: %b", (int64_t) sp->socket, (double) st, (double) et, (double) irp->interval_duration, (int64_t) irp->bytes_transferred, bandwidth * 8, irp->omitted, sp->sender));
+ else
+ iperf_printf(test, report_bw_format, sp->socket, mbuf, st, et, ubuf, nbuf, irp->omitted?report_omitted:"");
+ }
+ } else {
+ /* Interval, UDP. */
+ if (sp->sender) {
+ 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 packets: %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_packet_count, irp->omitted, sp->sender));
+ else
+ iperf_printf(test, report_bw_udp_sender_format, sp->socket, mbuf, st, et, ubuf, nbuf, zbuf, irp->interval_packet_count, irp->omitted?report_omitted:"");
+ } else {
+ if (irp->interval_packet_count > 0) {
+ lost_percent = 100.0 * irp->interval_cnt_error / irp->interval_packet_count;
+ }
+ else {
+ lost_percent = 0.0;
+ }
+ 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 jitter_ms: %f lost_packets: %d packets: %d lost_percent: %f omitted: %b sender: %b", (int64_t) sp->socket, (double) st, (double) et, (double) irp->interval_duration, (int64_t) irp->bytes_transferred, bandwidth * 8, (double) irp->jitter * 1000.0, (int64_t) irp->interval_cnt_error, (int64_t) irp->interval_packet_count, (double) lost_percent, irp->omitted, sp->sender));
+ else
+ iperf_printf(test, report_bw_udp_format, sp->socket, mbuf, st, et, ubuf, nbuf, irp->jitter * 1000.0, irp->interval_cnt_error, irp->interval_packet_count, lost_percent, irp->omitted?report_omitted:"");
+ }
+ }
+
+ if (test->logfile || test->forceflush)
+ iflush(test);
+}
+
+/**************************************************************************/
+void
+iperf_free_stream(struct iperf_stream *sp)
+{
+ struct iperf_interval_results *irp, *nirp;
+
+ /* XXX: need to free interval list too! */
+ munmap(sp->buffer, sp->test->settings->blksize);
+ close(sp->buffer_fd);
+ if (sp->diskfile_fd >= 0)
+ close(sp->diskfile_fd);
+ for (irp = TAILQ_FIRST(&sp->result->interval_results); irp != NULL; irp = nirp) {
+ nirp = TAILQ_NEXT(irp, irlistentries);
+ free(irp);
+ }
+ free(sp->result);
+ if (sp->send_timer != NULL)
+ tmr_cancel(sp->send_timer);
+ free(sp);
+}
+
+/**************************************************************************/
+struct iperf_stream *
+iperf_new_stream(struct iperf_test *test, int s, int sender)
+{
+ struct iperf_stream *sp;
+ int ret = 0;
+
+ char template[1024];
+ if (test->tmp_template) {
+ snprintf(template, sizeof(template) / sizeof(char), "%s", test->tmp_template);
+ } else {
+ //find the system temporary dir *unix, windows, cygwin support
+ char* tempdir = getenv("TMPDIR");
+ if (tempdir == 0){
+ tempdir = getenv("TEMP");
+ }
+ if (tempdir == 0){
+ tempdir = getenv("TMP");
+ }
+ if (tempdir == 0){
+#if defined(__ANDROID__)
+ tempdir = "/data/local/tmp";
+#else
+ tempdir = "/tmp";
+#endif
+ }
+ snprintf(template, sizeof(template) / sizeof(char), "%s/iperf3.XXXXXX", tempdir);
+ }
+
+ sp = (struct iperf_stream *) malloc(sizeof(struct iperf_stream));
+ if (!sp) {
+ i_errno = IECREATESTREAM;
+ return NULL;
+ }
+
+ memset(sp, 0, sizeof(struct iperf_stream));
+
+ sp->sender = sender;
+ sp->test = test;
+ sp->settings = test->settings;
+ sp->result = (struct iperf_stream_result *) malloc(sizeof(struct iperf_stream_result));
+ if (!sp->result) {
+ free(sp);
+ i_errno = IECREATESTREAM;
+ return NULL;
+ }
+
+ memset(sp->result, 0, sizeof(struct iperf_stream_result));
+ TAILQ_INIT(&sp->result->interval_results);
+
+ /* Create and randomize the buffer */
+ sp->buffer_fd = mkstemp(template);
+ if (sp->buffer_fd == -1) {
+ i_errno = IECREATESTREAM;
+ free(sp->result);
+ free(sp);
+ return NULL;
+ }
+ if (unlink(template) < 0) {
+ i_errno = IECREATESTREAM;
+ free(sp->result);
+ free(sp);
+ return NULL;
+ }
+ if (ftruncate(sp->buffer_fd, test->settings->blksize) < 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);
+ if (sp->buffer == MAP_FAILED) {
+ i_errno = IECREATESTREAM;
+ free(sp->result);
+ free(sp);
+ return NULL;
+ }
+ sp->pending_size = 0;
+
+ /* Set socket */
+ sp->socket = s;
+
+ sp->snd = test->protocol->send;
+ sp->rcv = test->protocol->recv;
+
+ if (test->diskfile_name != (char*) 0) {
+ sp->diskfile_fd = open(test->diskfile_name, sender ? O_RDONLY : (O_WRONLY|O_CREAT|O_TRUNC), S_IRUSR|S_IWUSR);
+ if (sp->diskfile_fd == -1) {
+ i_errno = IEFILE;
+ munmap(sp->buffer, sp->test->settings->blksize);
+ free(sp->result);
+ free(sp);
+ return NULL;
+ }
+ sp->snd2 = sp->snd;
+ sp->snd = diskfile_send;
+ sp->rcv2 = sp->rcv;
+ sp->rcv = diskfile_recv;
+ } else
+ sp->diskfile_fd = -1;
+
+ /* Initialize stream */
+ if (test->repeating_payload)
+ fill_with_repeating_pattern(sp->buffer, test->settings->blksize);
+ else
+ ret = readentropy(sp->buffer, test->settings->blksize);
+
+ if ((ret < 0) || (iperf_init_stream(sp, test) < 0)) {
+ close(sp->buffer_fd);
+ munmap(sp->buffer, sp->test->settings->blksize);
+ free(sp->result);
+ free(sp);
+ return NULL;
+ }
+ iperf_add_stream(test, sp);
+
+ return sp;
+}
+
+/**************************************************************************/
+int
+iperf_common_sockopts(struct iperf_test *test, int s)
+{
+ int opt;
+
+ /* Set IP TOS */
+ if ((opt = test->settings->tos)) {
+ if (getsockdomain(s) == AF_INET6) {
+#ifdef IPV6_TCLASS
+ if (setsockopt(s, IPPROTO_IPV6, IPV6_TCLASS, &opt, sizeof(opt)) < 0) {
+ i_errno = IESETCOS;
+ return -1;
+ }
+
+ /* if the control connection was established with a mapped v4 address
+ then set IP_TOS on v6 stream socket as well */
+ if (iperf_get_mapped_v4(test)) {
+ if (setsockopt(s, IPPROTO_IP, IP_TOS, &opt, sizeof(opt)) < 0) {
+ /* ignore any failure of v4 TOS in IPv6 case */
+ }
+ }
+#else
+ i_errno = IESETCOS;
+ return -1;
+#endif
+ } else {
+ if (setsockopt(s, IPPROTO_IP, IP_TOS, &opt, sizeof(opt)) < 0) {
+ i_errno = IESETTOS;
+ return -1;
+ }
+ }
+ }
+ return 0;
+}
+
+/**************************************************************************/
+int
+iperf_init_stream(struct iperf_stream *sp, struct iperf_test *test)
+{
+ int opt;
+ socklen_t len;
+
+ len = sizeof(struct sockaddr_storage);
+ if (getsockname(sp->socket, (struct sockaddr *) &sp->local_addr, &len) < 0) {
+ i_errno = IEINITSTREAM;
+ return -1;
+ }
+ len = sizeof(struct sockaddr_storage);
+ if (getpeername(sp->socket, (struct sockaddr *) &sp->remote_addr, &len) < 0) {
+ i_errno = IEINITSTREAM;
+ return -1;
+ }
+
+#if defined(HAVE_DONT_FRAGMENT)
+ /* Set Don't Fragment (DF). Only applicable to IPv4/UDP tests. */
+ if (iperf_get_test_protocol_id(test) == Pudp &&
+ getsockdomain(sp->socket) == AF_INET &&
+ iperf_get_dont_fragment(test)) {
+
+ /*
+ * There are multiple implementations of this feature depending on the OS.
+ * We need to handle separately Linux, UNIX, and Windows, as well as
+ * the case that DF isn't supported at all (such as on macOS).
+ */
+#if defined(IP_MTU_DISCOVER) /* Linux version of IP_DONTFRAG */
+ opt = IP_PMTUDISC_DO;
+ if (setsockopt(sp->socket, IPPROTO_IP, IP_MTU_DISCOVER, &opt, sizeof(opt)) < 0) {
+ i_errno = IESETDONTFRAGMENT;
+ return -1;
+ }
+#else
+#if defined(IP_DONTFRAG) /* UNIX does IP_DONTFRAG */
+ opt = 1;
+ if (setsockopt(sp->socket, IPPROTO_IP, IP_DONTFRAG, &opt, sizeof(opt)) < 0) {
+ i_errno = IESETDONTFRAGMENT;
+ return -1;
+ }
+#else
+#if defined(IP_DONTFRAGMENT) /* Windows does IP_DONTFRAGMENT */
+ opt = 1;
+ if (setsockopt(sp->socket, IPPROTO_IP, IP_DONTFRAGMENT, &opt, sizeof(opt)) < 0) {
+ i_errno = IESETDONTFRAGMENT;
+ return -1;
+ }
+#else
+ i_errno = IESETDONTFRAGMENT;
+ return -1;
+#endif /* IP_DONTFRAGMENT */
+#endif /* IP_DONTFRAG */
+#endif /* IP_MTU_DISCOVER */
+ }
+#endif /* HAVE_DONT_FRAGMENT */
+
+ return 0;
+}
+
+/**************************************************************************/
+void
+iperf_add_stream(struct iperf_test *test, struct iperf_stream *sp)
+{
+ int i;
+ struct iperf_stream *n, *prev;
+
+ if (SLIST_EMPTY(&test->streams)) {
+ SLIST_INSERT_HEAD(&test->streams, sp, streams);
+ sp->id = 1;
+ } else {
+ // for (n = test->streams, i = 2; n->next; n = n->next, ++i);
+ // NOTE: this would ideally be set to 1, however this will not
+ // be changed since it is not causing a significant problem
+ // 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;
+ }
+ if (prev) {
+ SLIST_INSERT_AFTER(prev, sp, streams);
+ sp->id = i;
+ }
+ }
+}
+
+/* This pair of routines gets inserted into the snd/rcv function pointers
+** when there's a -F flag. They handle the file stuff and call the real
+** snd/rcv functions, which have been saved in snd2/rcv2.
+**
+** The advantage of doing it this way is that in the much more common
+** case of no -F flag, there is zero extra overhead.
+*/
+
+static int
+diskfile_send(struct iperf_stream *sp)
+{
+ int r;
+ int buffer_left = sp->diskfile_left; // represents total data in buffer to be sent out
+ static int rtot;
+
+ /* if needed, read enough data from the disk to fill up the buffer */
+ if (sp->diskfile_left < sp->test->settings->blksize && !sp->test->done) {
+ r = read(sp->diskfile_fd, sp->buffer, sp->test->settings->blksize -
+ sp->diskfile_left);
+ buffer_left += r;
+ rtot += r;
+ if (sp->test->debug) {
+ printf("read %d bytes from file, %d total\n", r, rtot);
+ }
+
+ // If the buffer doesn't contain a full buffer at this point,
+ // adjust the size of the data to send.
+ if (buffer_left != sp->test->settings->blksize) {
+ if (sp->test->debug)
+ printf("possible eof\n");
+ // setting data size to be sent,
+ // which is less than full block/buffer size
+ // (to be used by iperf_tcp_send, etc.)
+ sp->pending_size = buffer_left;
+ }
+
+ // If there's no work left, we're done.
+ if (buffer_left == 0) {
+ sp->test->done = 1;
+ if (sp->test->debug)
+ printf("done\n");
+ }
+ }
+
+ // If there's no data left in the file or in the buffer, we're done.
+ // No more data available to be sent.
+ // Return without sending data to the network
+ if( sp->test->done || buffer_left == 0 ){
+ if (sp->test->debug)
+ printf("already done\n");
+ sp->test->done = 1;
+ return 0;
+ }
+
+ r = sp->snd2(sp);
+ if (r < 0) {
+ return r;
+ }
+ /*
+ * Compute how much data is in the buffer but didn't get sent.
+ * If there are bytes that got left behind, slide them to the
+ * front of the buffer so they can hopefully go out on the next
+ * pass.
+ */
+ sp->diskfile_left = buffer_left - r;
+ if (sp->diskfile_left && sp->diskfile_left < sp->test->settings->blksize) {
+ memcpy(sp->buffer,
+ sp->buffer + (sp->test->settings->blksize - sp->diskfile_left),
+ sp->diskfile_left);
+ if (sp->test->debug)
+ printf("Shifting %d bytes by %d\n", sp->diskfile_left, (sp->test->settings->blksize - sp->diskfile_left));
+ }
+ return r;
+}
+
+static int
+diskfile_recv(struct iperf_stream *sp)
+{
+ int r;
+
+ r = sp->rcv2(sp);
+ if (r > 0) {
+ // NOTE: Currently ignoring the return value of writing to disk
+ (void) (write(sp->diskfile_fd, sp->buffer, r) + 1);
+ }
+ return r;
+}
+
+
+void
+iperf_catch_sigend(void (*handler)(int))
+{
+#ifdef SIGINT
+ signal(SIGINT, handler);
+#endif
+#ifdef SIGTERM
+ signal(SIGTERM, handler);
+#endif
+#ifdef SIGHUP
+ signal(SIGHUP, handler);
+#endif
+}
+
+/**
+ * Called as a result of getting a signal.
+ * Depending on the current state of the test (and the role of this
+ * process) compute and report one more set of ending statistics
+ * before cleaning up and exiting.
+ */
+void
+iperf_got_sigend(struct iperf_test *test)
+{
+ /*
+ * If we're the client, or if we're a server and running a test,
+ * then dump out the accumulated stats so far.
+ */
+ if (test->role == 'c' ||
+ (test->role == 's' && test->state == TEST_RUNNING)) {
+
+ test->done = 1;
+ cpu_util(test->cpu_util);
+ test->stats_callback(test);
+ test->state = 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;
+ (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));
+}
+
+/* Try to write a PID file if requested, return -1 on an error. */
+int
+iperf_create_pidfile(struct iperf_test *test)
+{
+ if (test->pidfile) {
+ int fd;
+ char buf[8];
+
+ /* See if the file already exists and we can read it. */
+ fd = open(test->pidfile, O_RDONLY, 0);
+ if (fd >= 0) {
+ if (read(fd, buf, sizeof(buf) - 1) >= 0) {
+
+ /* We read some bytes, see if they correspond to a valid PID */
+ pid_t pid;
+ pid = atoi(buf);
+ if (pid > 0) {
+
+ /* See if the process exists. */
+ if (kill(pid, 0) == 0) {
+ /*
+ * Make sure not to try to delete existing PID file by
+ * scribbling over the pathname we'd use to refer to it.
+ * Then exit with an error.
+ */
+ free(test->pidfile);
+ test->pidfile = NULL;
+ iperf_errexit(test, "Another instance of iperf3 appears to be running");
+ }
+ }
+ }
+ }
+
+ /*
+ * File didn't exist, we couldn't read it, or it didn't correspond to
+ * a running process. Try to create it.
+ */
+ fd = open(test->pidfile, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR|S_IWUSR);
+ if (fd < 0) {
+ return -1;
+ }
+ snprintf(buf, sizeof(buf), "%d", getpid()); /* no trailing newline */
+ if (write(fd, buf, strlen(buf)) < 0) {
+ (void)close(fd);
+ return -1;
+ }
+ if (close(fd) < 0) {
+ return -1;
+ };
+ }
+ return 0;
+}
+
+/* Get rid of a PID file, return -1 on error. */
+int
+iperf_delete_pidfile(struct iperf_test *test)
+{
+ if (test->pidfile) {
+ if (unlink(test->pidfile) < 0) {
+ return -1;
+ }
+ }
+ return 0;
+}
+
+int
+iperf_json_start(struct iperf_test *test)
+{
+ test->json_top = cJSON_CreateObject();
+ if (test->json_top == NULL)
+ return -1;
+ test->json_start = cJSON_CreateObject();
+ if (test->json_start == NULL)
+ return -1;
+ cJSON_AddItemToObject(test->json_top, "start", test->json_start);
+ test->json_connected = cJSON_CreateArray();
+ if (test->json_connected == NULL)
+ return -1;
+ cJSON_AddItemToObject(test->json_start, "connected", test->json_connected);
+ test->json_intervals = cJSON_CreateArray();
+ if (test->json_intervals == NULL)
+ return -1;
+ cJSON_AddItemToObject(test->json_top, "intervals", test->json_intervals);
+ test->json_end = cJSON_CreateObject();
+ if (test->json_end == NULL)
+ return -1;
+ cJSON_AddItemToObject(test->json_top, "end", test->json_end);
+ return 0;
+}
+
+int
+iperf_json_finish(struct iperf_test *test)
+{
+ 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);
+ }
+ // 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 (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);
+ test->json_top = NULL;
+ }
+ test->json_start = test->json_connected = test->json_intervals = test->json_server_output = test->json_end = NULL;
+ return 0;
+}
+
+
+/* CPU affinity stuff - Linux, FreeBSD, and Windows only. */
+
+int
+iperf_setaffinity(struct iperf_test *test, int affinity)
+{
+#if defined(HAVE_SCHED_SETAFFINITY)
+ cpu_set_t cpu_set;
+
+ CPU_ZERO(&cpu_set);
+ CPU_SET(affinity, &cpu_set);
+ if (sched_setaffinity(0, sizeof(cpu_set_t), &cpu_set) != 0) {
+ i_errno = IEAFFINITY;
+ return -1;
+ }
+ return 0;
+#elif defined(HAVE_CPUSET_SETAFFINITY)
+ cpuset_t cpumask;
+
+ if(cpuset_getaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID, -1,
+ sizeof(cpuset_t), &test->cpumask) != 0) {
+ i_errno = IEAFFINITY;
+ return -1;
+ }
+
+ CPU_ZERO(&cpumask);
+ CPU_SET(affinity, &cpumask);
+
+ if(cpuset_setaffinity(CPU_LEVEL_WHICH,CPU_WHICH_PID, -1,
+ sizeof(cpuset_t), &cpumask) != 0) {
+ i_errno = IEAFFINITY;
+ return -1;
+ }
+ return 0;
+#elif defined(HAVE_SETPROCESSAFFINITYMASK)
+ HANDLE process = GetCurrentProcess();
+ DWORD_PTR processAffinityMask = 1 << affinity;
+
+ if (SetProcessAffinityMask(process, processAffinityMask) == 0) {
+ i_errno = IEAFFINITY;
+ return -1;
+ }
+ return 0;
+#else /* neither HAVE_SCHED_SETAFFINITY nor HAVE_CPUSET_SETAFFINITY nor HAVE_SETPROCESSAFFINITYMASK */
+ i_errno = IEAFFINITY;
+ return -1;
+#endif /* neither HAVE_SCHED_SETAFFINITY nor HAVE_CPUSET_SETAFFINITY nor HAVE_SETPROCESSAFFINITYMASK */
+}
+
+int
+iperf_clearaffinity(struct iperf_test *test)
+{
+#if defined(HAVE_SCHED_SETAFFINITY)
+ cpu_set_t cpu_set;
+ int i;
+
+ CPU_ZERO(&cpu_set);
+ for (i = 0; i < CPU_SETSIZE; ++i)
+ CPU_SET(i, &cpu_set);
+ if (sched_setaffinity(0, sizeof(cpu_set_t), &cpu_set) != 0) {
+ i_errno = IEAFFINITY;
+ return -1;
+ }
+ return 0;
+#elif defined(HAVE_CPUSET_SETAFFINITY)
+ if(cpuset_setaffinity(CPU_LEVEL_WHICH,CPU_WHICH_PID, -1,
+ sizeof(cpuset_t), &test->cpumask) != 0) {
+ i_errno = IEAFFINITY;
+ return -1;
+ }
+ return 0;
+#elif defined(HAVE_SETPROCESSAFFINITYMASK)
+ HANDLE process = GetCurrentProcess();
+ DWORD_PTR processAffinityMask;
+ DWORD_PTR lpSystemAffinityMask;
+
+ if (GetProcessAffinityMask(process, &processAffinityMask, &lpSystemAffinityMask) == 0
+ || SetProcessAffinityMask(process, lpSystemAffinityMask) == 0) {
+ i_errno = IEAFFINITY;
+ return -1;
+ }
+ return 0;
+#else /* neither HAVE_SCHED_SETAFFINITY nor HAVE_CPUSET_SETAFFINITY nor HAVE_SETPROCESSAFFINITYMASK */
+ i_errno = IEAFFINITY;
+ return -1;
+#endif /* neither HAVE_SCHED_SETAFFINITY nor HAVE_CPUSET_SETAFFINITY nor HAVE_SETPROCESSAFFINITYMASK */
+}
+
+static char iperf_timestr[100];
+static char linebuffer[1024];
+
+int
+iperf_printf(struct iperf_test *test, const char* format, ...)
+{
+ va_list argp;
+ int r = 0, r0;
+ time_t now;
+ 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);
+ ltm = localtime(&now);
+ strftime(iperf_timestr, sizeof(iperf_timestr), iperf_get_test_timestamp_format(test), ltm);
+ ct = iperf_timestr;
+ }
+
+ /*
+ * There are roughly two use cases here. If we're the client,
+ * want to print stuff directly to the output stream.
+ * If we're the sender we might need to buffer up output to send
+ * to the client.
+ *
+ * This doesn't make a whole lot of difference except there are
+ * some chunks of output on the client (on particular the whole
+ * of the server output with --get-server-output) that could
+ * easily exceed the size of the line buffer, but which don't need
+ * to be buffered up anyway.
+ */
+ if (test->role == 'c') {
+ if (ct) {
+ r0 = fprintf(test->outfile, "%s", ct);
+ if (r0 < 0) {
+ r = r0;
+ goto bottom;
+ }
+ r += r0;
+ }
+ if (test->title) {
+ r0 = fprintf(test->outfile, "%s: ", test->title);
+ 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) {
+ r = r0;
+ goto bottom;
+ }
+ r += r0;
+ }
+ else if (test->role == 's') {
+ if (ct) {
+ r0 = snprintf(linebuffer, sizeof(linebuffer), "%s", ct);
+ if (r0 < 0) {
+ r = r0;
+ goto bottom;
+ }
+ r += r0;
+ }
+ /* Should always be true as long as sizeof(ct) < sizeof(linebuffer) */
+ if (r < sizeof(linebuffer)) {
+ va_start(argp, format);
+ r0 = vsnprintf(linebuffer + r, sizeof(linebuffer) - r, format, argp);
+ va_end(argp);
+ if (r0 < 0) {
+ r = r0;
+ goto bottom;
+ }
+ r += r0;
+ }
+ fprintf(test->outfile, "%s", linebuffer);
+
+ if (test->role == 's' && iperf_get_test_get_server_output(test)) {
+ struct iperf_textline *l = (struct iperf_textline *) malloc(sizeof(struct iperf_textline));
+ l->line = strdup(linebuffer);
+ 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)
+{
+ 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;
+}
diff --git a/src/iperf_api.h b/src/iperf_api.h
new file mode 100644
index 0000000..9e70d44
--- /dev/null
+++ b/src/iperf_api.h
@@ -0,0 +1,497 @@
+/*
+ * 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.
+ *
+ * If you have questions about your rights to use or distribute this
+ * software, please contact Berkeley Lab's Technology Transfer
+ * Department at TTD@lbl.gov.
+ *
+ * NOTICE. This software is owned by the U.S. Department of Energy.
+ * As such, the U.S. Government has been granted for itself and others
+ * acting on its behalf a paid-up, nonexclusive, irrevocable,
+ * worldwide license in the Software to reproduce, prepare derivative
+ * works, and perform publicly and display publicly. Beginning five
+ * (5) years after the date permission to assert copyright is obtained
+ * from the U.S. Department of Energy, and subject to any subsequent
+ * five (5) year renewals, the U.S. Government is granted for itself
+ * and others acting on its behalf a paid-up, nonexclusive,
+ * irrevocable, worldwide license in the Software to reproduce,
+ * prepare derivative works, distribute copies to the public, perform
+ * publicly and display publicly, and to permit others to do so.
+ *
+ * This code is distributed under a BSD style license, see the LICENSE
+ * file for complete information.
+ */
+#ifndef __IPERF_API_H
+#define __IPERF_API_H
+
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <setjmp.h>
+#include <stdio.h>
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#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 <stdatomic.h>
+#else
+#warning "No <stdatomic.h> available"
+typedef u_int64_t atomic_uint_fast64_t;
+#endif // HAVE_STDATOMIC_H
+
+struct iperf_test;
+struct iperf_stream_result;
+struct iperf_interval_results;
+struct iperf_stream;
+struct iperf_time;
+
+#if !defined(__IPERF_H)
+typedef uint_fast64_t iperf_size_t;
+typedef atomic_uint_fast64_t atomic_iperf_size_t;
+#endif // __IPERF_H
+
+/* default settings */
+#define Ptcp SOCK_STREAM
+#define Pudp SOCK_DGRAM
+#define Psctp 12
+#define DEFAULT_UDP_BLKSIZE 1460 /* default is dynamically set, else this */
+#define DEFAULT_TCP_BLKSIZE (128 * 1024) /* default read/write block size */
+#define DEFAULT_SCTP_BLKSIZE (64 * 1024)
+#define DEFAULT_PACING_TIMER 1000
+#define DEFAULT_NO_MSG_RCVD_TIMEOUT 120000
+#define MIN_NO_MSG_RCVD_TIMEOUT 100
+
+#define WARN_STR_LEN 128
+
+/* short option equivalents, used to support options that only have long form */
+#define OPT_SCTP 1
+#define OPT_LOGFILE 2
+#define OPT_GET_SERVER_OUTPUT 3
+#define OPT_UDP_COUNTERS_64BIT 4
+#define OPT_CLIENT_PORT 5
+#define OPT_NUMSTREAMS 6
+#define OPT_FORCEFLUSH 7
+#define OPT_NO_FQ_SOCKET_PACING 9 /* UNUSED */
+#define OPT_FQ_RATE 10
+#define OPT_DSCP 11
+#define OPT_CLIENT_USERNAME 12
+#define OPT_CLIENT_RSA_PUBLIC_KEY 13
+#define OPT_SERVER_RSA_PRIVATE_KEY 14
+#define OPT_SERVER_AUTHORIZED_USERS 15
+#define OPT_PACING_TIMER 16
+#define OPT_CONNECT_TIMEOUT 17
+#define OPT_REPEATING_PAYLOAD 18
+#define OPT_EXTRA_DATA 19
+#define OPT_BIDIRECTIONAL 20
+#define OPT_SERVER_BITRATE_LIMIT 21
+#define OPT_TIMESTAMPS 22
+#define OPT_SERVER_SKEW_THRESHOLD 23
+#define OPT_BIND_DEV 24
+#define OPT_IDLE_TIMEOUT 25
+#define OPT_DONT_FRAGMENT 26
+#define OPT_RCV_TIMEOUT 27
+#define OPT_SND_TIMEOUT 28
+
+/* states */
+#define TEST_START 1
+#define TEST_RUNNING 2
+#define RESULT_REQUEST 3 /* not used */
+#define TEST_END 4
+#define STREAM_BEGIN 5 /* not used */
+#define STREAM_RUNNING 6 /* not used */
+#define STREAM_END 7 /* not used */
+#define ALL_STREAMS_END 8 /* not used */
+#define PARAM_EXCHANGE 9
+#define CREATE_STREAMS 10
+#define SERVER_TERMINATE 11
+#define CLIENT_TERMINATE 12
+#define EXCHANGE_RESULTS 13
+#define DISPLAY_RESULTS 14
+#define IPERF_START 15
+#define IPERF_DONE 16
+#define ACCESS_DENIED (-1)
+#define SERVER_ERROR (-2)
+
+/* Getter routines for some fields inside iperf_test. */
+int iperf_get_verbose( struct iperf_test* ipt );
+int iperf_get_control_socket( struct iperf_test* ipt );
+int iperf_get_test_omit( struct iperf_test* ipt );
+int iperf_get_test_duration( struct iperf_test* ipt );
+char iperf_get_test_role( struct iperf_test* ipt );
+int iperf_get_test_reverse( struct iperf_test* ipt );
+int iperf_get_test_bidirectional( struct iperf_test* ipt );
+int iperf_get_test_blksize( struct iperf_test* ipt );
+FILE* iperf_get_test_outfile( struct iperf_test* ipt );
+uint64_t iperf_get_test_rate( struct iperf_test* ipt );
+int iperf_get_test_pacing_timer( struct iperf_test* ipt );
+uint64_t iperf_get_test_bytes( struct iperf_test* ipt );
+uint64_t iperf_get_test_blocks( struct iperf_test* ipt );
+int iperf_get_test_burst( struct iperf_test* ipt );
+int iperf_get_test_socket_bufsize( struct iperf_test* ipt );
+double iperf_get_test_reporter_interval( struct iperf_test* ipt );
+double iperf_get_test_stats_interval( struct iperf_test* ipt );
+int iperf_get_test_num_streams( struct iperf_test* ipt );
+int iperf_get_test_repeating_payload( struct iperf_test* ipt );
+int iperf_get_test_timestamps( struct iperf_test* ipt );
+const char* iperf_get_test_timestamp_format( struct iperf_test* ipt );
+int iperf_get_test_bind_port( struct iperf_test* ipt );
+int iperf_get_test_server_port( struct iperf_test* ipt );
+char* iperf_get_test_server_hostname( struct iperf_test* ipt );
+char* iperf_get_test_template( struct iperf_test* ipt );
+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_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);
+char* iperf_get_test_bind_address ( struct iperf_test* ipt );
+char* iperf_get_test_bind_dev(struct iperf_test *ipt);
+int iperf_get_test_udp_counters_64bit( struct iperf_test* ipt );
+int iperf_get_test_one_off( struct iperf_test* ipt );
+int iperf_get_test_tos( struct iperf_test* ipt );
+char* iperf_get_extra_data( struct iperf_test* ipt );
+char* iperf_get_iperf_version(void);
+int iperf_get_test_no_delay( struct iperf_test* ipt );
+int iperf_get_test_connect_timeout( struct iperf_test* ipt );
+int iperf_get_dont_fragment( struct iperf_test* ipt );
+char* iperf_get_test_congestion_control(struct iperf_test* ipt);
+int iperf_get_test_mss(struct iperf_test* ipt);
+int iperf_get_mapped_v4(struct iperf_test* ipt);
+
+/* Setter routines for some fields inside iperf_test. */
+void iperf_set_verbose( struct iperf_test* ipt, int verbose );
+void iperf_set_control_socket( struct iperf_test* ipt, int ctrl_sck );
+void iperf_set_test_omit( struct iperf_test* ipt, int omit );
+void iperf_set_test_duration( struct iperf_test* ipt, int duration );
+void iperf_set_test_reporter_interval( struct iperf_test* ipt, double reporter_interval );
+void iperf_set_test_stats_interval( struct iperf_test* ipt, double stats_interval );
+void iperf_set_test_state( struct iperf_test* ipt, signed char state );
+void iperf_set_test_blksize( struct iperf_test* ipt, int blksize );
+void iperf_set_test_logfile( struct iperf_test* ipt, const char *logfile );
+void iperf_set_test_rate( struct iperf_test* ipt, uint64_t rate );
+void iperf_set_test_pacing_timer( struct iperf_test* ipt, int pacing_timer );
+void iperf_set_test_bytes( struct iperf_test* ipt, uint64_t bytes );
+void iperf_set_test_blocks( struct iperf_test* ipt, uint64_t blocks );
+void iperf_set_test_burst( struct iperf_test* ipt, int burst );
+void iperf_set_test_bind_port( struct iperf_test* ipt, int bind_port );
+void iperf_set_test_server_port( struct iperf_test* ipt, int server_port );
+void iperf_set_test_socket_bufsize( struct iperf_test* ipt, int socket_bufsize );
+void iperf_set_test_num_streams( struct iperf_test* ipt, int num_streams );
+void iperf_set_test_repeating_payload( struct iperf_test* ipt, int repeating_payload );
+void iperf_set_test_timestamps( struct iperf_test* ipt, int timestamps );
+void iperf_set_test_timestamp_format( struct iperf_test*, const char *tf );
+void iperf_set_test_role( struct iperf_test* ipt, char role );
+void iperf_set_test_server_hostname( struct iperf_test* ipt, const char* server_hostname );
+void iperf_set_test_template( struct iperf_test *ipt, const char *tmp_template );
+void iperf_set_test_reverse( struct iperf_test* ipt, int reverse );
+void iperf_set_test_json_output( struct iperf_test* ipt, int json_output );
+int iperf_has_zerocopy( void );
+void iperf_set_test_zerocopy( struct iperf_test* ipt, int zerocopy );
+void iperf_set_test_get_server_output( struct iperf_test* ipt, int get_server_output );
+void iperf_set_test_unit_format(struct iperf_test *ipt, char unit_format);
+void iperf_set_test_bind_address( struct iperf_test* ipt, const char *bind_address );
+void iperf_set_test_bind_dev(struct iperf_test *ipt, const char *bnd_dev);
+void iperf_set_test_udp_counters_64bit( struct iperf_test* ipt, int udp_counters_64bit );
+void iperf_set_test_one_off( struct iperf_test* ipt, int one_off );
+void iperf_set_test_tos( struct iperf_test* ipt, int tos );
+void iperf_set_test_extra_data( struct iperf_test* ipt, const char *dat );
+void iperf_set_test_bidirectional( struct iperf_test* ipt, int bidirectional);
+void iperf_set_test_no_delay( struct iperf_test* ipt, int no_delay);
+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)());
+void iperf_set_on_test_start_callback(struct iperf_test* ipt, void (*callback)());
+void iperf_set_on_test_connect_callback(struct iperf_test* ipt, void (*callback)());
+void iperf_set_on_test_finish_callback(struct iperf_test* ipt, void (*callback)());
+
+#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_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);
+#endif // HAVE_SSL
+
+void iperf_set_test_connect_timeout(struct iperf_test *ipt, int ct);
+
+/**
+ * exchange_parameters - handles the param_Exchange part for client
+ *
+ */
+int iperf_exchange_parameters(struct iperf_test * test);
+
+/**
+ * add_to_interval_list -- adds new interval to the interval_list
+ *
+ */
+void add_to_interval_list(struct iperf_stream_result * rp, struct iperf_interval_results *temp);
+
+/**
+ * connect_msg -- displays connection message
+ * denoting senfer/receiver details
+ *
+ */
+void connect_msg(struct iperf_stream * sp);
+
+/**
+ * iperf_stats_callback -- handles the statistic gathering
+ *
+ */
+void iperf_stats_callback(struct iperf_test * test);
+
+/**
+ * iperf_reporter_callback -- handles the report printing
+ *
+ */
+void iperf_reporter_callback(struct iperf_test * test);
+
+/**
+ * iperf_new_test -- return a new iperf_test with default values
+ *
+ * returns NULL on failure
+ *
+ */
+struct iperf_test *iperf_new_test(void);
+
+int iperf_defaults(struct iperf_test * testp);
+
+/**
+ * iperf_free_test -- free resources used by test, calls iperf_free_stream to
+ * free streams
+ *
+ */
+void iperf_free_test(struct iperf_test * testp);
+
+/**
+ * iperf_new_stream -- return a net iperf_stream with default values
+ *
+ * returns NULL on failure
+ *
+ */
+struct iperf_stream *iperf_new_stream(struct iperf_test *, int, int);
+
+/**
+ * iperf_add_stream -- add a stream to a test
+ *
+ */
+void iperf_add_stream(struct iperf_test * test, struct iperf_stream * stream);
+
+/**
+ * iperf_init_stream -- init resources associated with test
+ *
+ */
+int iperf_init_stream(struct iperf_stream *, struct iperf_test *);
+
+/**
+ * iperf_free_stream -- free resources associated with test
+ *
+ */
+void iperf_free_stream(struct iperf_stream * sp);
+
+/**
+ * iperf_common_sockopts -- init stream socket with common socket options
+ *
+ */
+int iperf_common_sockopts(struct iperf_test *, int s);
+
+int has_tcpinfo(void);
+int has_tcpinfo_retransmits(void);
+void save_tcpinfo(struct iperf_stream *sp, struct iperf_interval_results *irp);
+long get_total_retransmits(struct iperf_interval_results *irp);
+long get_snd_cwnd(struct iperf_interval_results *irp);
+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);
+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_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 usage(void);
+void usage_long(FILE * f);
+void warning(const char *);
+int iperf_exchange_results(struct iperf_test *);
+int iperf_init_test(struct iperf_test *);
+int iperf_create_send_timers(struct iperf_test *);
+int iperf_parse_arguments(struct iperf_test *, int, char **);
+int iperf_open_logfile(struct iperf_test *);
+void iperf_close_logfile(struct iperf_test *);
+void iperf_reset_test(struct iperf_test *);
+void iperf_reset_stats(struct iperf_test * test);
+
+struct protocol *get_protocol(struct iperf_test *, int);
+int set_protocol(struct iperf_test *, int);
+
+void iperf_on_new_stream(struct iperf_stream *);
+void iperf_on_test_start(struct iperf_test *);
+void iperf_on_connect(struct iperf_test *);
+void iperf_on_test_finish(struct iperf_test *);
+
+extern jmp_buf env;
+
+/* Client routines. */
+int iperf_run_client(struct iperf_test *);
+int iperf_connect(struct iperf_test *);
+int iperf_create_streams(struct iperf_test *, int sender);
+int iperf_handle_message_client(struct iperf_test *);
+int iperf_client_end(struct iperf_test *);
+
+/* Server routines. */
+int iperf_run_server(struct iperf_test *);
+int iperf_server_listen(struct iperf_test *);
+int iperf_accept(struct iperf_test *);
+int iperf_handle_message_server(struct iperf_test *);
+int iperf_create_pidfile(struct iperf_test *);
+int iperf_delete_pidfile(struct iperf_test *);
+void iperf_check_total_rate(struct iperf_test *, iperf_size_t);
+
+/* JSON output routines. */
+int iperf_json_start(struct iperf_test *);
+int iperf_json_finish(struct iperf_test *);
+
+/* CPU affinity routines */
+int iperf_setaffinity(struct iperf_test *, int affinity);
+int iperf_clearaffinity(struct iperf_test *);
+
+/* Custom printf routine. */
+int iperf_printf(struct iperf_test *test, const char *format, ...) __attribute__ ((format(printf,2,3)));
+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));
+char *iperf_strerror(int);
+extern int i_errno;
+enum {
+ IENONE = 0, // No error
+ /* Parameter errors */
+ IESERVCLIENT = 1, // Iperf cannot be both server and client
+ IENOROLE = 2, // Iperf must either be a client (-c) or server (-s)
+ IESERVERONLY = 3, // This option is server only
+ IECLIENTONLY = 4, // This option is client only
+ IEDURATION = 5, // test duration too long. Maximum value = %dMAX_TIME
+ IENUMSTREAMS = 6, // Number of parallel streams too large. Maximum value = %dMAX_STREAMS
+ IEBLOCKSIZE = 7, // Block size too large. Maximum value = %dMAX_BLOCKSIZE
+ IEBUFSIZE = 8, // Socket buffer size too large. Maximum value = %dMAX_TCP_BUFFER
+ IEINTERVAL = 9, // Invalid report interval (min = %gMIN_INTERVAL, max = %gMAX_INTERVAL seconds)
+ IEMSS = 10, // MSS too large. Maximum value = %dMAX_MSS
+ IENOSENDFILE = 11, // This OS does not support sendfile
+ IEOMIT = 12, // Bogus value for --omit
+ IEUNIMP = 13, // Not implemented yet
+ IEFILE = 14, // -F file couldn't be opened
+ IEBURST = 15, // Invalid burst count. Maximum value = %dMAX_BURST
+ IEENDCONDITIONS = 16, // Only one test end condition (-t, -n, -k) may be specified
+ IELOGFILE = 17, // Can't open log file
+ IENOSCTP = 18, // No SCTP support available
+ IEBIND = 19, // UNUSED: Local port specified with no local bind option
+ IEUDPBLOCKSIZE = 20, // Block size invalid
+ IEBADTOS = 21, // Bad TOS value
+ IESETCLIENTAUTH = 22, // Bad configuration of client authentication
+ IESETSERVERAUTH = 23, // Bad configuration of server authentication
+ IEBADFORMAT = 24, // Bad format argument to -f
+ IEREVERSEBIDIR = 25, // Iperf cannot be both reverse and bidirectional
+ IEBADPORT = 26, // Bad port number
+ IETOTALRATE = 27, // Total required bandwidth is larger than server's limit
+ IETOTALINTERVAL = 28, // Invalid time interval for calculating average data rate
+ IESKEWTHRESHOLD = 29, // Invalid value specified as skew threshold
+ IEIDLETIMEOUT = 30, // Invalid value specified as idle state timeout
+ IERCVTIMEOUT = 31, // Illegal message receive timeout
+ 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
+ /* Test errors */
+ IENEWTEST = 100, // Unable to create a new test (check perror)
+ IEINITTEST = 101, // Test initialization failed (check perror)
+ IELISTEN = 102, // Unable to listen for connections (check perror)
+ IECONNECT = 103, // Unable to connect to server (check herror/perror) [from netdial]
+ IEACCEPT = 104, // Unable to accept connection from client (check herror/perror)
+ IESENDCOOKIE = 105, // Unable to send cookie to server (check perror)
+ IERECVCOOKIE = 106, // Unable to receive cookie from client (check perror)
+ IECTRLWRITE = 107, // Unable to write to the control socket (check perror)
+ IECTRLREAD = 108, // Unable to read from the control socket (check perror)
+ IECTRLCLOSE = 109, // Control socket has closed unexpectedly
+ IEMESSAGE = 110, // Received an unknown message
+ IESENDMESSAGE = 111, // Unable to send control message to client/server (check perror)
+ IERECVMESSAGE = 112, // Unable to receive control message from client/server (check perror)
+ IESENDPARAMS = 113, // Unable to send parameters to server (check perror)
+ IERECVPARAMS = 114, // Unable to receive parameters from client (check perror)
+ IEPACKAGERESULTS = 115, // Unable to package results (check perror)
+ IESENDRESULTS = 116, // Unable to send results to client/server (check perror)
+ IERECVRESULTS = 117, // Unable to receive results from client/server (check perror)
+ IESELECT = 118, // Select failed (check perror)
+ IECLIENTTERM = 119, // The client has terminated
+ IESERVERTERM = 120, // The server has terminated
+ IEACCESSDENIED = 121, // The server is busy running a test. Try again later.
+ IESETNODELAY = 122, // Unable to set TCP/SCTP NODELAY (check perror)
+ IESETMSS = 123, // Unable to set TCP/SCTP MSS (check perror)
+ IESETBUF = 124, // Unable to set socket buffer size (check perror)
+ IESETTOS = 125, // Unable to set IP TOS (check perror)
+ IESETCOS = 126, // Unable to set IPv6 traffic class (check perror)
+ IESETFLOW = 127, // Unable to set IPv6 flow label
+ IEREUSEADDR = 128, // Unable to set reuse address on socket (check perror)
+ IENONBLOCKING = 129, // Unable to set socket to non-blocking (check perror)
+ IESETWINDOWSIZE = 130, // Unable to set socket window size (check perror)
+ IEPROTOCOL = 131, // Protocol does not exist
+ IEAFFINITY = 132, // Unable to set CPU affinity (check perror)
+ IEDAEMON = 133, // Unable to become a daemon process
+ IESETCONGESTION = 134, // Unable to set TCP_CONGESTION
+ IEPIDFILE = 135, // Unable to write PID file
+ IEV6ONLY = 136, // Unable to set/unset IPV6_V6ONLY (check perror)
+ IESETSCTPDISABLEFRAG = 137, // Unable to set SCTP Fragmentation (check perror)
+ IESETSCTPNSTREAM= 138, // Unable to set SCTP number of streams (check perror)
+ IESETSCTPBINDX= 139, // Unable to process sctp_bindx() parameters
+ IESETPACING= 140, // Unable to set socket pacing rate
+ IESETBUF2= 141, // Socket buffer size incorrect (written value != read value)
+ IEAUTHTEST = 142, // Test authorization failed
+ IEBINDDEV = 143, // Unable to bind-to-device (check perror, maybe permissions?)
+ IENOMSG = 144, // No message was received for NO_MSG_RCVD_TIMEOUT time period
+ IESETDONTFRAGMENT = 145, // Unable to set IP Do-Not-Fragment
+ IEBINDDEVNOSUPPORT = 146, // `ip%%dev` is not supported as system does not support bind to device
+ IEHOSTDEV = 147, // host device name (ip%%<dev>) 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)
+ /* 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)
+ 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
+ IESTREAMID = 208, // Stream has invalid ID
+ /* Timer errors */
+ IENEWTIMER = 300, // Unable to create new timer (check perror)
+ IEUPDATETIMER = 301, // Unable to update timer (check perror)
+};
+
+
+#ifdef __cplusplus
+} /* close extern "C" */
+#endif
+
+
+#endif /* !__IPERF_API_H */
diff --git a/src/iperf_auth.c b/src/iperf_auth.c
new file mode 100644
index 0000000..c89bde7
--- /dev/null
+++ b/src/iperf_auth.c
@@ -0,0 +1,438 @@
+/*
+ * 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.
+ *
+ * If you have questions about your rights to use or distribute this
+ * software, please contact Berkeley Lab's Technology Transfer
+ * Department at TTD@lbl.gov.
+ *
+ * NOTICE. This software is owned by the U.S. Department of Energy.
+ * As such, the U.S. Government has been granted for itself and others
+ * acting on its behalf a paid-up, nonexclusive, irrevocable,
+ * worldwide license in the Software to reproduce, prepare derivative
+ * works, and perform publicly and display publicly. Beginning five
+ * (5) years after the date permission to assert copyright is obtained
+ * from the U.S. Department of Energy, and subject to any subsequent
+ * five (5) year renewals, the U.S. Government is granted for itself
+ * and others acting on its behalf a paid-up, nonexclusive,
+ * irrevocable, worldwide license in the Software to reproduce,
+ * prepare derivative works, distribute copies to the public, perform
+ * publicly and display publicly, and to permit others to do so.
+ *
+ * This code is distributed under a BSD style license, see the LICENSE file
+ * for complete information.
+ */
+
+#include "iperf_config.h"
+
+#include <string.h>
+#include <assert.h>
+#include <time.h>
+#include <sys/types.h>
+/* FreeBSD needs _WITH_GETLINE to enable the getline() declaration */
+#define _WITH_GETLINE
+#include <stdio.h>
+#include <termios.h>
+#include <inttypes.h>
+#include <stdint.h>
+
+#if defined(HAVE_SSL)
+
+#include <openssl/rsa.h>
+#include <openssl/bio.h>
+#include <openssl/pem.h>
+#include <openssl/sha.h>
+#include <openssl/buffer.h>
+#include <openssl/err.h>
+#if OPENSSL_VERSION_MAJOR >= 3
+#include <openssl/evp.h>
+#include <openssl/core_names.h>
+#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((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]);
+ }
+ outputBuffer[64] = 0;
+}
+
+int check_authentication(const char *username, const char *password, const time_t ts, const char *filename, int skew_threshold){
+ time_t t = time(NULL);
+ time_t utc_seconds = mktime(localtime(&t));
+ if ( (utc_seconds - ts) > skew_threshold || (utc_seconds - ts) < -skew_threshold ) {
+ return 1;
+ }
+
+ char passwordHash[65];
+ char salted[strlen(username) + strlen(password) + 3];
+ sprintf(salted, "{%s}%s", username, password);
+ sha256(&salted[0], passwordHash);
+
+ char *s_username, *s_password;
+ int i;
+ FILE *ptr_file;
+ char buf[1024];
+
+ ptr_file =fopen(filename,"r");
+ if (!ptr_file)
+ return 2;
+
+ while (fgets(buf,1024, ptr_file)){
+ //strip the \n or \r\n chars
+ for (i = 0; buf[i] != '\0'; i++){
+ if (buf[i] == '\n' || buf[i] == '\r'){
+ buf[i] = '\0';
+ break;
+ }
+ }
+ //skip empty / not completed / comment lines
+ if (strlen(buf) == 0 || strchr(buf, ',') == NULL || buf[0] == '#'){
+ continue;
+ }
+ s_username = strtok(buf, ",");
+ s_password = strtok(NULL, ",");
+ if (strcmp( username, s_username ) == 0 && strcmp( passwordHash, s_password ) == 0){
+ fclose(ptr_file);
+ return 0;
+ }
+ }
+ fclose(ptr_file);
+ return 3;
+}
+
+
+int Base64Encode(const unsigned char* buffer, const size_t length, char** b64text) { //Encodes a binary safe base 64 string
+ BIO *bio, *b64;
+ BUF_MEM *bufferPtr;
+
+ b64 = BIO_new(BIO_f_base64());
+ bio = BIO_new(BIO_s_mem());
+ bio = BIO_push(b64, bio);
+
+ BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL); //Ignore newlines - write everything in one line
+ BIO_write(bio, buffer, length);
+ BIO_flush(bio);
+ BIO_get_mem_ptr(bio, &bufferPtr);
+ *b64text = strndup( (*bufferPtr).data, (*bufferPtr).length );
+ BIO_free_all(bio);
+
+ return (0); //success
+}
+
+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 =
+ padding = 2;
+ else if (b64input[len-1] == '=') //last char is =
+ padding = 1;
+
+ return (len*3)/4 - padding;
+}
+
+int Base64Decode(const char* b64message, unsigned char** buffer, size_t* length) { //Decodes a base64 encoded string
+ BIO *bio, *b64;
+
+ int decodeLen = calcDecodeLength(b64message);
+ *buffer = (unsigned char*)malloc(decodeLen + 1);
+ (*buffer)[decodeLen] = '\0';
+
+ bio = BIO_new_mem_buf(b64message, -1);
+ b64 = BIO_new(BIO_f_base64());
+ bio = BIO_push(b64, bio);
+
+ 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
+}
+
+EVP_PKEY *load_pubkey_from_file(const char *file) {
+ BIO *key = NULL;
+ EVP_PKEY *pkey = NULL;
+
+ if (file) {
+ key = BIO_new_file(file, "r");
+ if (key != NULL) {
+ pkey = PEM_read_bio_PUBKEY(key, NULL, NULL, NULL);
+ BIO_free(key);
+ }
+ }
+ return (pkey);
+}
+
+EVP_PKEY *load_pubkey_from_base64(const char *buffer) {
+ unsigned char *key = NULL;
+ size_t key_len;
+ Base64Decode(buffer, &key, &key_len);
+
+ BIO* bio = BIO_new(BIO_s_mem());
+ BIO_write(bio, key, key_len);
+ free(key);
+ EVP_PKEY *pkey = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL);
+ BIO_free(bio);
+ return (pkey);
+}
+
+EVP_PKEY *load_privkey_from_file(const char *file) {
+ BIO *key = NULL;
+ EVP_PKEY *pkey = NULL;
+
+ if (file) {
+ key = BIO_new_file(file, "r");
+ if (key != NULL) {
+ pkey = PEM_read_bio_PrivateKey(key, NULL, NULL, NULL);
+ BIO_free(key);
+ }
+ }
+ return (pkey);
+}
+
+EVP_PKEY *load_privkey_from_base64(const char *buffer) {
+ unsigned char *key = NULL;
+ size_t key_len;
+ Base64Decode(buffer, &key, &key_len);
+
+ BIO* bio = BIO_new(BIO_s_mem());
+ BIO_write(bio, key, key_len);
+ free(key);
+ EVP_PKEY *pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
+ BIO_free(bio);
+ return (pkey);
+}
+
+int test_load_pubkey_from_file(const char *file){
+ EVP_PKEY *key = load_pubkey_from_file(file);
+ if (key == NULL){
+ return -1;
+ }
+ EVP_PKEY_free(key);
+ return 0;
+}
+
+int test_load_private_key_from_file(const char *file){
+ EVP_PKEY *key = load_privkey_from_file(file);
+ if (key == NULL){
+ return -1;
+ }
+ EVP_PKEY_free(key);
+ return 0;
+}
+
+int encrypt_rsa_message(const char *plaintext, EVP_PKEY *public_key, unsigned char **encryptedtext) {
+#if OPENSSL_VERSION_MAJOR >= 3
+ EVP_PKEY_CTX *ctx;
+#else
+ RSA *rsa = NULL;
+#endif
+ unsigned char *rsa_buffer = NULL;
+ size_t encryptedtext_len = 0;
+ int rsa_buffer_len, keysize;
+
+#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, &keysize); /* XXX not really keysize */
+ if (!rc) {
+ goto errreturn;
+ }
+#else
+ rsa = EVP_PKEY_get1_RSA(public_key);
+ keysize = RSA_size(rsa);
+#endif
+ rsa_buffer = OPENSSL_malloc(keysize * 2);
+ *encryptedtext = (unsigned char*)OPENSSL_malloc(keysize);
+
+ BIO *bioBuff = BIO_new_mem_buf((void*)plaintext, (int)strlen(plaintext));
+ rsa_buffer_len = BIO_read(bioBuff, rsa_buffer, keysize * 2);
+#if OPENSSL_VERSION_MAJOR >= 3
+ EVP_PKEY_encrypt_init(ctx);
+ EVP_PKEY_encrypt(ctx, *encryptedtext, &encryptedtext_len, rsa_buffer, rsa_buffer_len);
+ EVP_PKEY_CTX_free(ctx);
+#else
+ encryptedtext_len = RSA_public_encrypt(rsa_buffer_len, rsa_buffer, *encryptedtext, rsa, RSA_PKCS1_PADDING);
+ RSA_free(rsa);
+#endif
+
+ OPENSSL_free(rsa_buffer);
+ BIO_free(bioBuff);
+
+ 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) {
+#if OPENSSL_VERSION_MAJOR >= 3
+ EVP_PKEY_CTX *ctx;
+#else
+ RSA *rsa = NULL;
+#endif
+ unsigned char *rsa_buffer = NULL;
+ size_t plaintext_len = 0;
+ int rsa_buffer_len, keysize;
+
+#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, &keysize); /* XXX not really keysize */
+ if (!rc) {
+ goto errreturn;
+ }
+#else
+ rsa = EVP_PKEY_get1_RSA(private_key);
+ keysize = RSA_size(rsa);
+#endif
+ rsa_buffer = OPENSSL_malloc(keysize * 2);
+ *plaintext = (unsigned char*)OPENSSL_malloc(keysize);
+
+ BIO *bioBuff = BIO_new_mem_buf((void*)encryptedtext, encryptedtext_len);
+ rsa_buffer_len = BIO_read(bioBuff, rsa_buffer, keysize * 2);
+#if OPENSSL_VERSION_MAJOR >= 3
+ plaintext_len = keysize;
+ EVP_PKEY_decrypt_init(ctx);
+ EVP_PKEY_decrypt(ctx, *plaintext, &plaintext_len, rsa_buffer, rsa_buffer_len);
+ EVP_PKEY_CTX_free(ctx);
+#else
+ plaintext_len = RSA_private_decrypt(rsa_buffer_len, rsa_buffer, *plaintext, rsa, RSA_PKCS1_PADDING);
+ RSA_free(rsa);
+#endif
+
+ OPENSSL_free(rsa_buffer);
+ BIO_free(bioBuff);
+
+ if (plaintext_len <= 0) {
+ goto errreturn;
+ }
+
+ return plaintext_len;
+
+ errreturn:
+ fprintf(stderr, "%s\n", ERR_error_string(ERR_get_error(), NULL));
+ return 0;
+}
+
+int encode_auth_setting(const char *username, const char *password, EVP_PKEY *public_key, char **authtoken){
+ time_t t = time(NULL);
+ time_t utc_seconds = mktime(localtime(&t));
+
+ /*
+ * Compute a pessimistic/conservative estimate of storage required.
+ * It's OK to allocate too much storage but too little is bad.
+ */
+ 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;
+ }
+ 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);
+ free(text);
+ if (encrypted_len < 0) {
+ return -1;
+ }
+ Base64Encode(encrypted, encrypted_len, authtoken);
+ OPENSSL_free(encrypted);
+
+ return (0); //success
+}
+
+int decode_auth_setting(int enable_debug, const char *authtoken, EVP_PKEY *private_key, char **username, char **password, time_t *ts){
+ unsigned char *encrypted_b64 = NULL;
+ size_t encrypted_len_b64;
+ int64_t utc_seconds;
+ 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);
+ free(encrypted_b64);
+ 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;
+ }
+ s_password = (char *) calloc(plaintext_len, sizeof(char));
+ if (s_password == NULL) {
+ 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;
+ }
+
+ if (enable_debug) {
+ printf("Auth Token Content:\n%s\n", plaintext);
+ printf("Auth Token Credentials:\n--> %s %s\n", s_username, s_password);
+ }
+ *username = s_username;
+ *password = s_password;
+ *ts = (time_t)utc_seconds;
+ OPENSSL_free(plaintext);
+ return (0);
+}
+
+#endif //HAVE_SSL
+
+ssize_t iperf_getpass (char **lineptr, size_t *n, FILE *stream) {
+ struct termios old, new;
+ ssize_t nread;
+
+ /* Turn echoing off and fail if we can't. */
+ if (tcgetattr (fileno (stream), &old) != 0)
+ return -1;
+ new = old;
+ new.c_lflag &= ~ECHO;
+ if (tcsetattr (fileno (stream), TCSAFLUSH, &new) != 0)
+ return -1;
+
+ /* Read the password. */
+ printf("Password: ");
+ nread = getline (lineptr, n, stream);
+
+ /* Restore terminal. */
+ (void) tcsetattr (fileno (stream), TCSAFLUSH, &old);
+
+ //strip the \n or \r\n chars
+ char *buf = *lineptr;
+ int i;
+ for (i = 0; buf[i] != '\0'; i++){
+ if (buf[i] == '\n' || buf[i] == '\r'){
+ buf[i] = '\0';
+ break;
+ }
+ }
+
+ return nread;
+}
diff --git a/src/iperf_auth.h b/src/iperf_auth.h
new file mode 100644
index 0000000..ffadbf3
--- /dev/null
+++ b/src/iperf_auth.h
@@ -0,0 +1,41 @@
+/*
+ * iperf, Copyright (c) 2014-2017, 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.
+ *
+ * If you have questions about your rights to use or distribute this
+ * software, please contact Berkeley Lab's Technology Transfer
+ * Department at TTD@lbl.gov.
+ *
+ * NOTICE. This software is owned by the U.S. Department of Energy.
+ * As such, the U.S. Government has been granted for itself and others
+ * acting on its behalf a paid-up, nonexclusive, irrevocable,
+ * worldwide license in the Software to reproduce, prepare derivative
+ * works, and perform publicly and display publicly. Beginning five
+ * (5) years after the date permission to assert copyright is obtained
+ * from the U.S. Department of Energy, and subject to any subsequent
+ * five (5) year renewals, the U.S. Government is granted for itself
+ * and others acting on its behalf a paid-up, nonexclusive,
+ * irrevocable, worldwide license in the Software to reproduce,
+ * prepare derivative works, distribute copies to the public, perform
+ * publicly and display publicly, and to permit others to do so.
+ *
+ * This code is distributed under a BSD style license, see the LICENSE file
+ * for complete information.
+ */
+
+#include <time.h>
+#include <sys/types.h>
+#include <openssl/bio.h>
+
+int test_load_pubkey_from_file(const char *public_keyfile);
+int test_load_private_key_from_file(const char *private_keyfile);
+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 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
new file mode 100644
index 0000000..5b33369
--- /dev/null
+++ b/src/iperf_client_api.c
@@ -0,0 +1,819 @@
+/*
+ * 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.
+ *
+ * If you have questions about your rights to use or distribute this
+ * software, please contact Berkeley Lab's Technology Transfer
+ * Department at TTD@lbl.gov.
+ *
+ * NOTICE. This software is owned by the U.S. Department of Energy.
+ * As such, the U.S. Government has been granted for itself and others
+ * acting on its behalf a paid-up, nonexclusive, irrevocable,
+ * worldwide license in the Software to reproduce, prepare derivative
+ * works, and perform publicly and display publicly. Beginning five
+ * (5) years after the date permission to assert copyright is obtained
+ * from the U.S. Department of Energy, and subject to any subsequent
+ * five (5) year renewals, the U.S. Government is granted for itself
+ * and others acting on its behalf a paid-up, nonexclusive,
+ * irrevocable, worldwide license in the Software to reproduce,
+ * prepare derivative works, distribute copies to the public, perform
+ * publicly and display publicly, and to permit others to do so.
+ *
+ * This code is distributed under a BSD style license, see the LICENSE
+ * file for complete information.
+ */
+#include <errno.h>
+#include <setjmp.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <sys/select.h>
+#include <sys/uio.h>
+#include <arpa/inet.h>
+
+#include "iperf.h"
+#include "iperf_api.h"
+#include "iperf_util.h"
+#include "iperf_locale.h"
+#include "iperf_time.h"
+#include "net.h"
+#include "timer.h"
+
+#if defined(HAVE_TCP_CONGESTION)
+#if !defined(TCP_CA_NAME_MAX)
+#define TCP_CA_NAME_MAX 16
+#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;
+
+ /* Allow this thread to be cancelled even if it's in a syscall */
+ pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, 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)
+{
+ if (NULL == test)
+ {
+ iperf_err(NULL, "No test\n");
+ return -1;
+ }
+ int i, s;
+#if defined(HAVE_TCP_CONGESTION)
+ int saved_errno;
+#endif /* HAVE_TCP_CONGESTION */
+ struct iperf_stream *sp;
+
+ int orig_bind_port = test->bind_port;
+ for (i = 0; i < test->num_streams; ++i) {
+
+ test->bind_port = orig_bind_port;
+ if (orig_bind_port) {
+ test->bind_port += i;
+ // If Bidir make sure send and receive ports are different
+ if (!sender && test->mode == BIDIRECTIONAL)
+ test->bind_port += test->num_streams;
+ }
+ s = test->protocol->connect(test);
+ test->bind_port = orig_bind_port;
+ if (s < 0)
+ return -1;
+
+#if defined(HAVE_TCP_CONGESTION)
+ if (test->protocol->id == Ptcp) {
+ if (test->congestion) {
+ if (setsockopt(s, IPPROTO_TCP, TCP_CONGESTION, test->congestion, strlen(test->congestion)) < 0) {
+ saved_errno = errno;
+ close(s);
+ errno = saved_errno;
+ i_errno = IESETCONGESTION;
+ return -1;
+ }
+ }
+ {
+ socklen_t len = TCP_CA_NAME_MAX;
+ char ca[TCP_CA_NAME_MAX + 1];
+ int rc;
+ rc = getsockopt(s, IPPROTO_TCP, TCP_CONGESTION, ca, &len);
+ if (rc < 0 && test->congestion) {
+ saved_errno = errno;
+ close(s);
+ errno = saved_errno;
+ i_errno = IESETCONGESTION;
+ return -1;
+ }
+ // Set actual used congestion alg, or set to unknown if could not get it
+ if (rc < 0)
+ test->congestion_used = strdup("unknown");
+ else
+ test->congestion_used = strdup(ca);
+ if (test->debug) {
+ printf("Congestion algorithm is %s\n", test->congestion_used);
+ }
+ }
+ }
+#endif /* HAVE_TCP_CONGESTION */
+
+ sp = iperf_new_stream(test, s, sender);
+ if (!sp)
+ return -1;
+
+ /* Perform the new stream callback */
+ if (test->on_new_stream)
+ test->on_new_stream(sp);
+ }
+
+ return 0;
+}
+
+static void
+test_timer_proc(TimerClientData client_data, struct iperf_time *nowP)
+{
+ struct iperf_test *test = client_data.p;
+
+ test->timer = NULL;
+ test->done = 1;
+}
+
+static void
+client_stats_timer_proc(TimerClientData client_data, struct iperf_time *nowP)
+{
+ struct iperf_test *test = client_data.p;
+
+ if (test->done)
+ return;
+ if (test->stats_callback)
+ test->stats_callback(test);
+}
+
+static void
+client_reporter_timer_proc(TimerClientData client_data, struct iperf_time *nowP)
+{
+ struct iperf_test *test = client_data.p;
+
+ if (test->done)
+ return;
+ if (test->reporter_callback)
+ test->reporter_callback(test);
+}
+
+static int
+create_client_timers(struct iperf_test * test)
+{
+ struct iperf_time now;
+ TimerClientData cd;
+ if (NULL == test)
+ {
+ iperf_err(NULL, "No test\n");
+ i_errno = IEINITTEST;
+ return -1;
+ }
+
+ if (iperf_time_now(&now) < 0) {
+ i_errno = IEINITTEST;
+ return -1;
+ }
+ cd.p = test;
+ test->timer = test->stats_timer = test->reporter_timer = NULL;
+ if (test->duration != 0) {
+ test->done = 0;
+ test->timer = tmr_create(&now, test_timer_proc, cd, ( test->duration + test->omit ) * SEC_TO_US, 0);
+ if (test->timer == NULL) {
+ i_errno = IEINITTEST;
+ return -1;
+ }
+ }
+ if (test->stats_interval != 0) {
+ test->stats_timer = tmr_create(&now, client_stats_timer_proc, cd, test->stats_interval * SEC_TO_US, 1);
+ if (test->stats_timer == NULL) {
+ i_errno = IEINITTEST;
+ return -1;
+ }
+ }
+ if (test->reporter_interval != 0) {
+ test->reporter_timer = tmr_create(&now, client_reporter_timer_proc, cd, test->reporter_interval * SEC_TO_US, 1);
+ if (test->reporter_timer == NULL) {
+ i_errno = IEINITTEST;
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static void
+client_omit_timer_proc(TimerClientData client_data, struct iperf_time *nowP)
+{
+ struct iperf_test *test = client_data.p;
+
+ test->omit_timer = NULL;
+ test->omitting = 0;
+ iperf_reset_stats(test);
+ if (test->verbose && !test->json_output && test->reporter_interval == 0)
+ iperf_printf(test, "%s", report_omit_done);
+
+ /* Reset the timers. */
+ if (test->stats_timer != NULL)
+ tmr_reset(nowP, test->stats_timer);
+ if (test->reporter_timer != NULL)
+ tmr_reset(nowP, test->reporter_timer);
+}
+
+static int
+create_client_omit_timer(struct iperf_test * test)
+{
+ struct iperf_time now;
+ TimerClientData cd;
+ if (NULL == test)
+ {
+ iperf_err(NULL, "No test\n");
+ return -1;
+ }
+
+ if (test->omit == 0) {
+ test->omit_timer = NULL;
+ test->omitting = 0;
+ } else {
+ if (iperf_time_now(&now) < 0) {
+ i_errno = IEINITTEST;
+ return -1;
+ }
+ test->omitting = 1;
+ cd.p = test;
+ test->omit_timer = tmr_create(&now, client_omit_timer_proc, cd, test->omit * SEC_TO_US, 0);
+ if (test->omit_timer == NULL) {
+ i_errno = IEINITTEST;
+ return -1;
+ }
+ }
+ return 0;
+}
+
+int
+iperf_handle_message_client(struct iperf_test *test)
+{
+ int rval;
+ int32_t err;
+
+ if (NULL == test)
+ {
+ iperf_err(NULL, "No test\n");
+ i_errno = IEINITTEST;
+ return -1;
+ }
+ /*!!! Why is this read() and not Nread()? */
+ if ((rval = read(test->ctrl_sck, (char*) &test->state, sizeof(signed char))) <= 0) {
+ if (rval == 0) {
+ i_errno = IECTRLCLOSE;
+ return -1;
+ } else {
+ i_errno = IERECVMESSAGE;
+ return -1;
+ }
+ }
+
+ switch (test->state) {
+ case PARAM_EXCHANGE:
+ if (iperf_exchange_parameters(test) < 0)
+ return -1;
+ if (test->on_connect)
+ test->on_connect(test);
+ break;
+ case CREATE_STREAMS:
+ if (test->mode == BIDIRECTIONAL)
+ {
+ if (iperf_create_streams(test, 1) < 0)
+ return -1;
+ if (iperf_create_streams(test, 0) < 0)
+ return -1;
+ }
+ else if (iperf_create_streams(test, test->mode) < 0)
+ return -1;
+ break;
+ case TEST_START:
+ if (iperf_init_test(test) < 0)
+ return -1;
+ if (create_client_timers(test) < 0)
+ return -1;
+ if (create_client_omit_timer(test) < 0)
+ return -1;
+ if (test->mode)
+ if (iperf_create_send_timers(test) < 0)
+ return -1;
+ break;
+ case TEST_RUNNING:
+ break;
+ case EXCHANGE_RESULTS:
+ if (iperf_exchange_results(test) < 0)
+ return -1;
+ break;
+ case DISPLAY_RESULTS:
+ if (test->on_test_finish)
+ test->on_test_finish(test);
+ iperf_client_end(test);
+ break;
+ case IPERF_DONE:
+ break;
+ case SERVER_TERMINATE:
+ i_errno = IESERVERTERM;
+
+ /*
+ * Temporarily be in DISPLAY_RESULTS phase so we can get
+ * ending summary statistics.
+ */
+ signed char oldstate = test->state;
+ cpu_util(test->cpu_util);
+ test->state = DISPLAY_RESULTS;
+ test->reporter_callback(test);
+ test->state = oldstate;
+ return -1;
+ case ACCESS_DENIED:
+ i_errno = IEACCESSDENIED;
+ return -1;
+ case SERVER_ERROR:
+ if (Nread(test->ctrl_sck, (char*) &err, sizeof(err), Ptcp) < 0) {
+ i_errno = IECTRLREAD;
+ return -1;
+ }
+ i_errno = ntohl(err);
+ if (Nread(test->ctrl_sck, (char*) &err, sizeof(err), Ptcp) < 0) {
+ i_errno = IECTRLREAD;
+ return -1;
+ }
+ errno = ntohl(err);
+ return -1;
+ default:
+ i_errno = IEMESSAGE;
+ return -1;
+ }
+
+ return 0;
+}
+
+
+
+/* iperf_connect -- client to server connection function */
+int
+iperf_connect(struct iperf_test *test)
+{
+ int opt;
+ socklen_t len;
+
+ if (NULL == test)
+ {
+ iperf_err(NULL, "No test\n");
+ return -1;
+ }
+ FD_ZERO(&test->read_set);
+ FD_ZERO(&test->write_set);
+
+ make_cookie(test->cookie);
+
+ /* Create and connect the control channel */
+ if (test->ctrl_sck < 0)
+ // Create the control channel using an ephemeral port
+ test->ctrl_sck = netdial(test->settings->domain, Ptcp, test->bind_address, test->bind_dev, 0, test->server_hostname, test->server_port, test->settings->connect_timeout);
+ if (test->ctrl_sck < 0) {
+ i_errno = IECONNECT;
+ return -1;
+ }
+
+ // set TCP_NODELAY for lower latency on control messages
+ int flag = 1;
+ if (setsockopt(test->ctrl_sck, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int))) {
+ i_errno = IESETNODELAY;
+ return -1;
+ }
+
+#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;
+ }
+ }
+#endif /* HAVE_TCP_USER_TIMEOUT */
+
+ if (Nwrite(test->ctrl_sck, test->cookie, COOKIE_SIZE, Ptcp) < 0) {
+ i_errno = IESENDCOOKIE;
+ return -1;
+ }
+
+ FD_SET(test->ctrl_sck, &test->read_set);
+ if (test->ctrl_sck > test->max_fd) test->max_fd = test->ctrl_sck;
+
+ len = sizeof(opt);
+ if (getsockopt(test->ctrl_sck, IPPROTO_TCP, TCP_MAXSEG, &opt, &len) < 0) {
+ test->ctrl_sck_mss = 0;
+ }
+ else {
+ if (opt > 0 && opt <= MAX_UDP_BLOCKSIZE) {
+ test->ctrl_sck_mss = opt;
+ }
+ else {
+ char str[WARN_STR_LEN];
+ snprintf(str, sizeof(str),
+ "Ignoring nonsense TCP MSS %d", opt);
+ warning(str);
+
+ test->ctrl_sck_mss = 0;
+ }
+ }
+
+ if (test->verbose) {
+ printf("Control connection MSS %d\n", test->ctrl_sck_mss);
+ }
+
+ /*
+ * If we're doing a UDP test and the block size wasn't explicitly
+ * set, then use the known MSS of the control connection to pick
+ * an appropriate default. If we weren't able to get the
+ * MSS for some reason, then default to something that should
+ * work on non-jumbo-frame Ethernet networks. The goal is to
+ * pick a reasonable default that is large but should get from
+ * sender to receiver without any IP fragmentation.
+ *
+ * We assume that the control connection is routed the same as the
+ * data packets (thus has the same PMTU). Also in the case of
+ * --reverse tests, we assume that the MTU is the same in both
+ * directions. Note that even if the algorithm guesses wrong,
+ * the user always has the option to override.
+ */
+ if (test->protocol->id == Pudp) {
+ if (test->settings->blksize == 0) {
+ if (test->ctrl_sck_mss) {
+ test->settings->blksize = test->ctrl_sck_mss;
+ }
+ else {
+ test->settings->blksize = DEFAULT_UDP_BLKSIZE;
+ }
+ if (test->verbose) {
+ printf("Setting UDP block size to %d\n", test->settings->blksize);
+ }
+ }
+
+ /*
+ * Regardless of whether explicitly or implicitly set, if the
+ * block size is larger than the MSS, print a warning.
+ */
+ if (test->ctrl_sck_mss > 0 &&
+ test->settings->blksize > test->ctrl_sck_mss) {
+ char str[WARN_STR_LEN];
+ snprintf(str, sizeof(str),
+ "UDP block size %d exceeds TCP MSS %d, may result in fragmentation / drops", test->settings->blksize, test->ctrl_sck_mss);
+ warning(str);
+ }
+ }
+
+ return 0;
+}
+
+
+int
+iperf_client_end(struct iperf_test *test)
+{
+ if (NULL == test)
+ {
+ iperf_err(NULL, "No test\n");
+ return -1;
+ }
+ struct iperf_stream *sp;
+
+ /* Close all stream sockets */
+ SLIST_FOREACH(sp, &test->streams, streams) {
+ close(sp->socket);
+ }
+
+ /* show final summary */
+ test->reporter_callback(test);
+
+ /* Send response only if no error in server */
+ if (test->state > 0) {
+ if (iperf_set_send_state(test, IPERF_DONE) != 0)
+ return -1;
+ }
+
+ /* Close control socket */
+ if (test->ctrl_sck >= 0)
+ close(test->ctrl_sck);
+
+ return 0;
+}
+
+
+int
+iperf_run_client(struct iperf_test * test)
+{
+ int startup;
+ int result = 0;
+ fd_set read_set, write_set;
+ struct iperf_time now;
+ struct timeval* timeout = NULL;
+ struct iperf_stream *sp;
+ 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)
+ {
+ iperf_err(NULL, "No test\n");
+ return -1;
+ }
+
+ if (test->logfile)
+ if (iperf_open_logfile(test) < 0)
+ return -1;
+
+ if (test->affinity != -1)
+ if (iperf_setaffinity(test, test->affinity) != 0)
+ return -1;
+
+ if (test->json_output)
+ if (iperf_json_start(test) < 0)
+ return -1;
+
+ if (test->json_output) {
+ cJSON_AddItemToObject(test->json_start, "version", cJSON_CreateString(version));
+ cJSON_AddItemToObject(test->json_start, "system_info", cJSON_CreateString(get_system_info()));
+ } else if (test->verbose) {
+ iperf_printf(test, "%s\n", version);
+ iperf_printf(test, "%s", "");
+ iperf_printf(test, "%s\n", get_system_info());
+ iflush(test);
+ }
+
+ /* Start the client and connect to the server */
+ if (iperf_connect(test) < 0)
+ goto cleanup_and_fail;
+
+ /* Begin calculating CPU utilization */
+ cpu_util(NULL);
+ if (test->mode != SENDER)
+ rcv_timeout_us = (test->settings->rcv_timeout.secs * SEC_TO_US) + test->settings->rcv_timeout.usecs;
+ 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));
+ memcpy(&write_set, &test->write_set, sizeof(fd_set));
+ iperf_time_now(&now);
+ timeout = tmr_timeout(&now);
+
+ // In reverse active mode client ensures data is received
+ if (test->state == TEST_RUNNING && rcv_timeout_us > 0) {
+ timeout_us = -1;
+ if (timeout != NULL) {
+ used_timeout.tv_sec = timeout->tv_sec;
+ 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;
+ }
+ timeout = &used_timeout;
+ }
+
+ result = select(test->max_fd + 1, &read_set, &write_set, NULL, timeout);
+ 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 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) {
+ /* 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 (FD_ISSET(test->ctrl_sck, &read_set)) {
+ if (iperf_handle_message_client(test) < 0) {
+ goto cleanup_and_fail;
+ }
+ FD_CLR(test->ctrl_sck, &read_set);
+ }
+ }
+
+ if (test->state == TEST_RUNNING) {
+
+ /* Is this our first time really running? */
+ if (startup) {
+ startup = 0;
+
+ /* Create and spin up threads */
+ pthread_attr_t attr;
+ if (pthread_attr_init(&attr) != 0) {
+ i_errno = IEPTHREADATTRINIT;
+ goto cleanup_and_fail;
+ }
+
+ 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;
+ }
+ 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);
+ tmr_run(&now);
+
+ /*
+ * Is the test done yet? We have to be out of omitting
+ * mode, and then we have to have fulfilled one of the
+ * ending criteria, either by times, bytes, or blocks.
+ * The bytes and blocks tests needs to handle both the
+ * cases of the client being the sender and the client
+ * being the receiver.
+ */
+ if ((!test->omitting) &&
+ (test->done ||
+ (test->settings->bytes != 0 && (test->bytes_sent >= test->settings->bytes ||
+ test->bytes_received >= test->settings->bytes)) ||
+ (test->settings->blocks != 0 && (test->blocks_sent >= test->settings->blocks ||
+ test->blocks_received >= test->settings->blocks)))) {
+
+ /* Cancel outstanding sender threads */
+ SLIST_FOREACH(sp, &test->streams, streams) {
+ if (sp->sender) {
+ int rc;
+ sp->done = 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);
+ }
+ }
+ }
+ if (test->debug_level >= DEBUG_LEVEL_INFO) {
+ iperf_printf(test, "Sender threads stopped\n");
+ }
+
+ /* Yes, done! Send TEST_END. */
+ test->done = 1;
+ cpu_util(test->cpu_util);
+ test->stats_callback(test);
+ if (iperf_set_send_state(test, TEST_END) != 0)
+ goto cleanup_and_fail;
+ }
+ }
+ }
+
+ /* Cancel outstanding receiver threads */
+ SLIST_FOREACH(sp, &test->streams, streams) {
+ if (!sp->sender) {
+ int rc;
+ sp->done = 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);
+ }
+ }
+ }
+ if (test->debug_level >= DEBUG_LEVEL_INFO) {
+ iperf_printf(test, "Receiver threads stopped\n");
+ }
+
+ if (test->json_output) {
+ if (iperf_json_finish(test) < 0)
+ return -1;
+ } else {
+ iperf_printf(test, "\n");
+ iperf_printf(test, "%s", report_done);
+ }
+
+ iflush(test);
+
+ return 0;
+
+ cleanup_and_fail:
+ /* Cancel all outstanding threads */
+ i_errno_save = i_errno;
+ SLIST_FOREACH(sp, &test->streams, streams) {
+ sp->done = 1;
+ int rc;
+ 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);
+ }
+ }
+ 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 -1;
+}
diff --git a/src/iperf_config.h.in b/src/iperf_config.h.in
new file mode 100644
index 0000000..bb8853f
--- /dev/null
+++ b/src/iperf_config.h.in
@@ -0,0 +1,160 @@
+/* src/iperf_config.h.in. Generated from configure.ac by autoheader. */
+
+/* Define to 1 if you have the `clock_gettime' function. */
+#undef HAVE_CLOCK_GETTIME
+
+/* 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. */
+#undef HAVE_DAEMON
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#undef HAVE_DLFCN_H
+
+/* Have IP_MTU_DISCOVER/IP_DONTFRAG/IP_DONTFRAGMENT sockopt. */
+#undef HAVE_DONT_FRAGMENT
+
+/* Define to 1 if you have the <endian.h> header file. */
+#undef HAVE_ENDIAN_H
+
+/* Have IPv6 flowlabel support. */
+#undef HAVE_FLOWLABEL
+
+/* Define to 1 if you have the `getline' function. */
+#undef HAVE_GETLINE
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#undef HAVE_INTTYPES_H
+
+/* Have IP_DONTFRAG sockopt. */
+#undef HAVE_IP_DONTFRAG
+
+/* Have IP_DONTFRAGMENT sockopt. */
+#undef HAVE_IP_DONTFRAGMENT
+
+/* Have IP_MTU_DISCOVER sockopt. */
+#undef HAVE_IP_MTU_DISCOVER
+
+/* Define to 1 if you have the <linux/tcp.h> header file. */
+#undef HAVE_LINUX_TCP_H
+
+/* Define to 1 if you have the <netinet/sctp.h> header file. */
+#undef HAVE_NETINET_SCTP_H
+
+/* Define to 1 if you have the <poll.h> header file. */
+#undef HAVE_POLL_H
+
+/* 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. */
+#undef HAVE_SENDFILE
+
+/* Define to 1 if you have the `SetProcessAffinityMask' function. */
+#undef HAVE_SETPROCESSAFFINITYMASK
+
+/* Have SO_BINDTODEVICE sockopt. */
+#undef HAVE_SO_BINDTODEVICE
+
+/* Have SO_MAX_PACING_RATE sockopt. */
+#undef HAVE_SO_MAX_PACING_RATE
+
+/* OpenSSL Is Available */
+#undef HAVE_SSL
+
+/* Define to 1 if you have the <stdatomic.h> header file. */
+#undef HAVE_STDATOMIC_H
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#undef HAVE_STDINT_H
+
+/* Define to 1 if you have the <stdio.h> header file. */
+#undef HAVE_STDIO_H
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#undef HAVE_STDLIB_H
+
+/* Define to 1 if you have the <strings.h> header file. */
+#undef HAVE_STRINGS_H
+
+/* Define to 1 if you have the <string.h> header file. */
+#undef HAVE_STRING_H
+
+/* 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 <sys/endian.h> header file. */
+#undef HAVE_SYS_ENDIAN_H
+
+/* Define to 1 if you have the <sys/socket.h> header file. */
+#undef HAVE_SYS_SOCKET_H
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#undef HAVE_SYS_STAT_H
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#undef HAVE_SYS_TYPES_H
+
+/* Have TCP_CONGESTION sockopt. */
+#undef HAVE_TCP_CONGESTION
+
+/* Have tcpi_snd_wnd field in tcp_info. */
+#undef HAVE_TCP_INFO_SND_WND
+
+/* Have TCP_USER_TIMEOUT sockopt. */
+#undef HAVE_TCP_USER_TIMEOUT
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#undef HAVE_UNISTD_H
+
+/* Define to the sub-directory where libtool stores uninstalled libraries. */
+#undef LT_OBJDIR
+
+/* Name of package */
+#undef PACKAGE
+
+/* Define to the address where bug reports for this package should be sent. */
+#undef PACKAGE_BUGREPORT
+
+/* Define to the full name of this package. */
+#undef PACKAGE_NAME
+
+/* Define to the full name and version of this package. */
+#undef PACKAGE_STRING
+
+/* Define to the one symbol short name of this package. */
+#undef PACKAGE_TARNAME
+
+/* Define to the home page for this package. */
+#undef PACKAGE_URL
+
+/* Define to the version of this package. */
+#undef PACKAGE_VERSION
+
+/* 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 C90 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
+
+/* Version number of package */
+#undef VERSION
+
+/* 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
new file mode 100644
index 0000000..6426554
--- /dev/null
+++ b/src/iperf_error.c
@@ -0,0 +1,527 @@
+/*
+ * iperf, Copyright (c) 2014-2022, 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.
+ *
+ * If you have questions about your rights to use or distribute this
+ * software, please contact Berkeley Lab's Technology Transfer
+ * Department at TTD@lbl.gov.
+ *
+ * NOTICE. This software is owned by the U.S. Department of Energy.
+ * As such, the U.S. Government has been granted for itself and others
+ * acting on its behalf a paid-up, nonexclusive, irrevocable,
+ * worldwide license in the Software to reproduce, prepare derivative
+ * works, and perform publicly and display publicly. Beginning five
+ * (5) years after the date permission to assert copyright is obtained
+ * from the U.S. Department of Energy, and subject to any subsequent
+ * five (5) year renewals, the U.S. Government is granted for itself
+ * and others acting on its behalf a paid-up, nonexclusive,
+ * irrevocable, worldwide license in the Software to reproduce,
+ * prepare derivative works, distribute copies to the public, perform
+ * publicly and display publicly, and to permit others to do so.
+ *
+ * This code is distributed under a BSD style license, see the LICENSE
+ * file for complete information.
+ */
+#include <stdio.h>
+#include <errno.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include "iperf.h"
+#include "iperf_api.h"
+
+int gerror;
+
+char iperf_timestrerr[100];
+
+/* Do a printf to stderr. */
+void
+iperf_err(struct iperf_test *test, const char *format, ...)
+{
+ va_list argp;
+ char str[1000];
+ time_t now;
+ struct tm *ltm = NULL;
+ char *ct = NULL;
+
+ if (pthread_mutex_lock(&(test->print_mutex)) != 0) {
+ perror("iperf_err: pthread_mutex_lock");
+ }
+
+ /* Timestamp if requested */
+ if (test != NULL && test->timestamps) {
+ time(&now);
+ ltm = localtime(&now);
+ strftime(iperf_timestrerr, sizeof(iperf_timestrerr), test->timestamp_format, ltm);
+ ct = iperf_timestrerr;
+ }
+
+ va_start(argp, format);
+ vsnprintf(str, sizeof(str), format, argp);
+ 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 (ct) {
+ fprintf(test->outfile, "%s", ct);
+ }
+ fprintf(test->outfile, "iperf3: %s\n", str);
+ }
+ else {
+ if (ct) {
+ fprintf(stderr, "%s", ct);
+ }
+ fprintf(stderr, "iperf3: %s\n", str);
+ }
+ }
+ va_end(argp);
+
+ if (pthread_mutex_unlock(&(test->print_mutex)) != 0) {
+ perror("iperf_err: pthread_mutex_unlock");
+ }
+}
+
+/* Do a printf to stderr or log file as appropriate, then exit. */
+void
+iperf_errexit(struct iperf_test *test, const char *format, ...)
+{
+ va_list argp;
+ char str[1000];
+ time_t now;
+ struct tm *ltm = NULL;
+ char *ct = NULL;
+
+ if (pthread_mutex_lock(&(test->print_mutex)) != 0) {
+ perror("iperf_errexit: pthread_mutex_lock");
+ }
+
+ /* Timestamp if requested */
+ if (test != NULL && test->timestamps) {
+ time(&now);
+ ltm = localtime(&now);
+ strftime(iperf_timestrerr, sizeof(iperf_timestrerr), "%c ", ltm);
+ ct = iperf_timestrerr;
+ }
+
+ va_start(argp, format);
+ vsnprintf(str, sizeof(str), format, argp);
+ if (test != NULL && test->json_output) {
+ if (test->json_top != NULL) {
+ cJSON_AddStringToObject(test->json_top, "error", str);
+ }
+ iperf_json_finish(test);
+ } else
+ if (test && test->outfile && test->outfile != stdout) {
+ if (ct) {
+ fprintf(test->outfile, "%s", ct);
+ }
+ fprintf(test->outfile, "iperf3: %s\n", str);
+ }
+ else {
+ if (ct) {
+ fprintf(stderr, "%s", ct);
+ }
+ fprintf(stderr, "iperf3: %s\n", str);
+ }
+
+ if (pthread_mutex_unlock(&(test->print_mutex)) != 0) {
+ perror("iperf_errexit: pthread_mutex_unlock");
+ }
+
+ va_end(argp);
+ if (test)
+ iperf_delete_pidfile(test);
+ exit(1);
+}
+
+int i_errno;
+
+char *
+iperf_strerror(int int_errno)
+{
+ static char errstr[256];
+ int len, perr, herr;
+ perr = herr = 0;
+
+ len = sizeof(errstr);
+ memset(errstr, 0, len);
+
+ switch (int_errno) {
+ case IENONE:
+ snprintf(errstr, len, "no error");
+ break;
+ case IESERVCLIENT:
+ snprintf(errstr, len, "cannot be both server and client");
+ break;
+ case IENOROLE:
+ snprintf(errstr, len, "must either be a client (-c) or server (-s)");
+ break;
+ case IESERVERONLY:
+ snprintf(errstr, len, "some option you are trying to set is server only");
+ break;
+ case IECLIENTONLY:
+ 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);
+ break;
+ case IENUMSTREAMS:
+ snprintf(errstr, len, "number of parallel streams too large (maximum = %d)", MAX_STREAMS);
+ break;
+ case IEBLOCKSIZE:
+ snprintf(errstr, len, "block size too large (maximum = %d bytes)", MAX_BLOCKSIZE);
+ break;
+ case IEBUFSIZE:
+ snprintf(errstr, len, "socket buffer size too large (maximum = %d bytes)", MAX_TCP_BUFFER);
+ break;
+ case IEINTERVAL:
+ snprintf(errstr, len, "invalid report interval (min = %g, max = %g seconds)", MIN_INTERVAL, MAX_INTERVAL);
+ break;
+ case IEBIND: /* UNUSED */
+ snprintf(errstr, len, "--bind must be specified to use --cport");
+ break;
+ case IEUDPBLOCKSIZE:
+ snprintf(errstr, len, "block size invalid (minimum = %d bytes, maximum = %d bytes)", MIN_UDP_BLOCKSIZE, MAX_UDP_BLOCKSIZE);
+ break;
+ case IEBADTOS:
+ snprintf(errstr, len, "bad TOS value (must be between 0 and 255 inclusive)");
+ break;
+ case IESETCLIENTAUTH:
+ snprintf(errstr, len, "you must specify a username, password, and path to a valid RSA public key");
+ break;
+ case IESETSERVERAUTH:
+ snprintf(errstr, len, "you must specify a path to a valid RSA private key and a user credential file");
+ 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;
+ case IENOSENDFILE:
+ snprintf(errstr, len, "this OS does not support sendfile");
+ break;
+ case IEOMIT:
+ snprintf(errstr, len, "bogus value for --omit");
+ break;
+ case IEUNIMP:
+ snprintf(errstr, len, "an option you are trying to set is not implemented yet");
+ break;
+ case IEFILE:
+ snprintf(errstr, len, "unable to open -F file");
+ perr = 1;
+ break;
+ case IEBURST:
+ snprintf(errstr, len, "invalid burst count (maximum = %d)", MAX_BURST);
+ break;
+ 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 IENEWTEST:
+ snprintf(errstr, len, "unable to create a new test");
+ perr = 1;
+ break;
+ case IEINITTEST:
+ snprintf(errstr, len, "test initialization failed");
+ perr = 1;
+ break;
+ case IEAUTHTEST:
+ snprintf(errstr, len, "test authorization failed");
+ break;
+ case IELISTEN:
+ snprintf(errstr, len, "unable to start listener for connections");
+ 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;
+ break;
+ case IEACCEPT:
+ snprintf(errstr, len, "unable to accept connection from client");
+ herr = 1;
+ perr = 1;
+ break;
+ case IESENDCOOKIE:
+ snprintf(errstr, len, "unable to send cookie to server");
+ perr = 1;
+ break;
+ case IERECVCOOKIE:
+ snprintf(errstr, len, "unable to receive cookie at server");
+ perr = 1;
+ break;
+ case IECTRLWRITE:
+ snprintf(errstr, len, "unable to write to the control socket");
+ perr = 1;
+ break;
+ case IECTRLREAD:
+ snprintf(errstr, len, "unable to read from the control socket");
+ perr = 1;
+ break;
+ case IECTRLCLOSE:
+ snprintf(errstr, len, "control socket has closed unexpectedly");
+ break;
+ case IEMESSAGE:
+ snprintf(errstr, len, "received an unknown control message (ensure other side is iperf3 and not iperf)");
+ break;
+ case IESENDMESSAGE:
+ snprintf(errstr, len, "unable to send control message - port may not be available, the other side may have stopped running, etc.");
+ perr = 1;
+ break;
+ case IERECVMESSAGE:
+ snprintf(errstr, len, "unable to receive control message - port may not be available, the other side may have stopped running, etc.");
+ perr = 1;
+ break;
+ case IESENDPARAMS:
+ snprintf(errstr, len, "unable to send parameters to server");
+ perr = 1;
+ break;
+ case IERECVPARAMS:
+ snprintf(errstr, len, "unable to receive parameters from client");
+ perr = 1;
+ break;
+ case IEPACKAGERESULTS:
+ snprintf(errstr, len, "unable to package results");
+ perr = 1;
+ break;
+ case IESENDRESULTS:
+ snprintf(errstr, len, "unable to send results");
+ perr = 1;
+ break;
+ case IERECVRESULTS:
+ snprintf(errstr, len, "unable to receive results");
+ perr = 1;
+ break;
+ case IESELECT:
+ snprintf(errstr, len, "select failed");
+ perr = 1;
+ break;
+ case IECLIENTTERM:
+ snprintf(errstr, len, "the client has terminated");
+ break;
+ case IESERVERTERM:
+ snprintf(errstr, len, "the server has terminated");
+ break;
+ case IEACCESSDENIED:
+ snprintf(errstr, len, "the server is busy running a test. try again later");
+ break;
+ case IESETNODELAY:
+ snprintf(errstr, len, "unable to set TCP/SCTP NODELAY");
+ perr = 1;
+ break;
+ case IESETMSS:
+ snprintf(errstr, len, "unable to set TCP/SCTP MSS");
+ perr = 1;
+ break;
+ case IESETBUF:
+ snprintf(errstr, len, "unable to set socket buffer size");
+ perr = 1;
+ break;
+ case IESETTOS:
+ snprintf(errstr, len, "unable to set IP TOS");
+ perr = 1;
+ break;
+ case IESETCOS:
+ snprintf(errstr, len, "unable to set IPv6 traffic class");
+ perr = 1;
+ break;
+ case IESETFLOW:
+ snprintf(errstr, len, "unable to set IPv6 flow label");
+ break;
+ case IEREUSEADDR:
+ snprintf(errstr, len, "unable to reuse address on socket");
+ perr = 1;
+ break;
+ case IENONBLOCKING:
+ snprintf(errstr, len, "unable to set socket to non-blocking");
+ perr = 1;
+ break;
+ case IESETWINDOWSIZE:
+ snprintf(errstr, len, "unable to set socket window size");
+ perr = 1;
+ break;
+ case IEPROTOCOL:
+ snprintf(errstr, len, "protocol does not exist");
+ break;
+ case IEAFFINITY:
+ snprintf(errstr, len, "unable to set CPU affinity");
+ perr = 1;
+ break;
+ case IERCVTIMEOUT:
+ snprintf(errstr, len, "receive timeout value is incorrect or not in range");
+ perr = 1;
+ break;
+ case IESNDTIMEOUT:
+ snprintf(errstr, len, "send timeout value is incorrect or not in range");
+ perr = 1;
+ break;
+ case IEUDPFILETRANSFER:
+ snprintf(errstr, len, "cannot transfer file using UDP");
+ 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 IECREATESTREAM:
+ snprintf(errstr, len, "unable to create a new stream");
+ herr = 1;
+ perr = 1;
+ break;
+ case IEINITSTREAM:
+ snprintf(errstr, len, "unable to initialize stream");
+ herr = 1;
+ perr = 1;
+ break;
+ case IESTREAMLISTEN:
+ snprintf(errstr, len, "unable to start stream listener");
+ herr = 1;
+ perr = 1;
+ break;
+ case IESTREAMCONNECT:
+ snprintf(errstr, len, "unable to connect stream");
+ herr = 1;
+ perr = 1;
+ break;
+ case IESTREAMACCEPT:
+ snprintf(errstr, len, "unable to accept stream connection");
+ perr = 1;
+ break;
+ case IESTREAMWRITE:
+ snprintf(errstr, len, "unable to write to stream socket");
+ perr = 1;
+ break;
+ case IESTREAMREAD:
+ snprintf(errstr, len, "unable to read from stream socket");
+ perr = 1;
+ break;
+ case IESTREAMCLOSE:
+ snprintf(errstr, len, "stream socket has closed unexpectedly");
+ break;
+ case IESTREAMID:
+ snprintf(errstr, len, "stream has an invalid id");
+ break;
+ case IENEWTIMER:
+ snprintf(errstr, len, "unable to create new timer");
+ perr = 1;
+ break;
+ case IEUPDATETIMER:
+ snprintf(errstr, len, "unable to update timer");
+ perr = 1;
+ break;
+ case IESETCONGESTION:
+ snprintf(errstr, len, "unable to set TCP_CONGESTION: "
+ "Supplied congestion control algorithm not supported on this host");
+ break;
+ 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 IESETSCTPDISABLEFRAG:
+ snprintf(errstr, len, "unable to set SCTP_DISABLE_FRAGMENTS");
+ perr = 1;
+ break;
+ case IESETSCTPNSTREAM:
+ 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");
+ break;
+ case IETOTALRATE:
+ snprintf(errstr, len, "total required bandwidth is larger than server limit");
+ break;
+ case IESKEWTHRESHOLD:
+ snprintf(errstr, len, "skew threshold must be a positive number");
+ break;
+ case IEIDLETIMEOUT:
+ snprintf(errstr, len, "idle timeout parameter is not positive or larger than allowed limit");
+ break;
+ case IEBINDDEV:
+ snprintf(errstr, len, "Unable to bind-to-device (check perror, maybe permissions?)");
+ break;
+ case IEBINDDEVNOSUPPORT:
+ snprintf(errstr, len, "`<ip>%%<dev>` is not supported as system does not support bind to device");
+ break;
+ case IEHOSTDEV:
+ snprintf(errstr, len, "host device name (ip%%<dev>) is supported (and required) only for IPv6 link-local address");
+ break;
+ case IENOMSG:
+ snprintf(errstr, len, "idle timeout for receiving data");
+ break;
+ 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;
+ 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 IEPTHREADATTRDESTROY:
+ snprintf(errstr, len, "unable to destroy thread attributes");
+ 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 */
+ if (herr || perr)
+ strncat(errstr, ": ", len - strlen(errstr) - 1);
+ if (errno && perr)
+ strncat(errstr, strerror(errno), len - strlen(errstr) - 1);
+ else if (herr && gerror) {
+ strncat(errstr, gai_strerror(gerror), len - strlen(errstr) - 1);
+ gerror = 0;
+ }
+
+ return errstr;
+}
diff --git a/src/iperf_locale.c b/src/iperf_locale.c
new file mode 100644
index 0000000..838086e
--- /dev/null
+++ b/src/iperf_locale.c
@@ -0,0 +1,568 @@
+/*---------------------------------------------------------------
+ * iperf, Copyright (c) 2014-2022, 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.
+ *
+ * If you have questions about your rights to use or distribute this
+ * software, please contact Berkeley Lab's Technology Transfer
+ * Department at TTD@lbl.gov.
+ *
+ * NOTICE. This software is owned by the U.S. Department of Energy.
+ * As such, the U.S. Government has been granted for itself and others
+ * acting on its behalf a paid-up, nonexclusive, irrevocable,
+ * worldwide license in the Software to reproduce, prepare derivative
+ * works, and perform publicly and display publicly. Beginning five
+ * (5) years after the date permission to assert copyright is obtained
+ * from the U.S. Department of Energy, and subject to any subsequent
+ * five (5) year renewals, the U.S. Government is granted for itself
+ * and others acting on its behalf a paid-up, nonexclusive,
+ * irrevocable, worldwide license in the Software to reproduce,
+ * prepare derivative works, distribute copies to the public, perform
+ * publicly and display publicly, and to permit others to do so.
+ *
+ * This code is distributed under a BSD style license, see the LICENSE
+ * file for complete information.
+ *
+ * Based on code that is:
+ *
+ * Copyright (c) 1999,2000,2001,2002,2003
+ * The Board of Trustees of the University of Illinois
+ * All Rights Reserved.
+ *---------------------------------------------------------------
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software (Iperf) and associated
+ * documentation files (the "Software"), to deal in the Software
+ * without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute,
+ * sublicense, and/or sell copies of the Software, and to permit
+ * persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ *
+ * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and
+ * the following disclaimers.
+ *
+ *
+ * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimers in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ *
+ * Neither the names of the University of Illinois, NCSA,
+ * nor the names of its contributors may be used to endorse
+ * or promote products derived from this Software without
+ * specific prior written permission.
+ *
+ * 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
+ * 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
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ * ________________________________________________________________
+ * National Laboratory for Applied Network Research
+ * National Center for Supercomputing Applications
+ * University of Illinois at Urbana-Champaign
+ * http://www.ncsa.uiuc.edu
+ * ________________________________________________________________
+ *
+ * Locale.c
+ * by Ajay Tirumala <tirumala@ncsa.uiuc.edu>
+ * & Mark Gates <mgates@nlanr.net>
+ * -------------------------------------------------------------------
+ * Strings and other stuff that is locale specific.
+ * ------------------------------------------------------------------- */
+#include "iperf_config.h"
+
+#include "version.h"
+
+#if defined(HAVE_INTTYPES_H)
+# include <inttypes.h>
+#else
+# ifndef PRIu64
+# if sizeof(long) == 8
+# define PRIu64 "lu"
+# else
+# define PRIu64 "llu"
+# endif
+# endif
+#endif
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+
+/* -------------------------------------------------------------------
+ * usage
+ * ------------------------------------------------------------------- */
+
+const char usage_shortstr[] = "Usage: iperf3 [-s|-c host] [options]\n"
+ "Try `iperf3 --help' for more information.\n";
+
+const char usage_longstr[] = "Usage: iperf3 [-s|-c host] [options]\n"
+ " iperf3 [-h|--help] [-v|--version]\n\n"
+ "Server or Client:\n"
+ " -p, --port # server port to listen on/connect to\n"
+ " -f, --format [kmgtKMGT] format to report: Kbits, Mbits, Gbits, Tbits\n"
+ " -i, --interval # seconds between periodic throughput reports\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"
+#endif /* HAVE_CPU_AFFINITY */
+#if defined(HAVE_SO_BINDTODEVICE)
+ " -B, --bind <host>[%%<dev>] bind to the interface associated with the address <host>\n"
+ " (optional <dev> equivalent to `--bind-dev <dev>`)\n"
+ " --bind-dev <dev> bind to the network interface with SO_BINDTODEVICE\n"
+#else /* HAVE_SO_BINDTODEVICE */
+ " -B, --bind <host> bind to the interface associated with the address <host>\n"
+#endif /* HAVE_SO_BINDTODEVICE */
+ " -V, --verbose more detailed output\n"
+ " -J, --json output in JSON format\n"
+ " --logfile f send output to a log file\n"
+ " --forceflush force flushing output at every interval\n"
+ " --timestamps<=format> emit a timestamp at the start of each output line\n"
+ " (optional \"=\" and format string as per strftime(3))\n"
+
+ " --rcv-timeout # idle timeout for receiving data (default %d ms)\n"
+#if defined(HAVE_TCP_USER_TIMEOUT)
+ " --snd-timeout # timeout for unacknowledged TCP data\n"
+ " (in ms, default is system settings)\n"
+#endif /* HAVE_TCP_USER_TIMEOUT */
+ " -d, --debug[=#] emit debugging output\n"
+ " (optional 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"
+ " -s, --server run in server mode\n"
+ " -D, --daemon run the server as a daemon\n"
+ " -1, --one-off handle one client connection then exit\n"
+ " --server-bitrate-limit #[KMG][/#] server's total bit rate limit (default 0 = no limit)\n"
+ " (optional slash and number of secs interval for averaging\n"
+ " total data rate. Default is 5 seconds)\n"
+ " --idle-timeout # restart idle server after # seconds in case it\n"
+ " got stuck (default - no timeout)\n"
+#if defined(HAVE_SSL)
+ " --rsa-private-key-path path to the RSA private key used to decrypt\n"
+ " authentication credentials\n"
+ " --authorized-users-path path to the configuration file containing user\n"
+ " credentials\n"
+ " --time-skew-threshold time skew threshold (in seconds) between the server\n"
+ " and client during the authentication process\n"
+#endif //HAVE_SSL
+ "Client specific:\n"
+ " -c, --client <host>[%%<dev>] run in client mode, connecting to <host>\n"
+ " (option <dev> equivalent to `--bind-dev <dev>`)\n"
+#if defined(HAVE_SCTP_H)
+ " --sctp use SCTP rather than TCP\n"
+ " -X, --xbind <name> bind SCTP association to links\n"
+ " --nstreams # number of SCTP streams\n"
+#endif /* HAVE_SCTP_H */
+ " -u, --udp use UDP rather than TCP\n"
+ " --connect-timeout # timeout for control connection setup (ms)\n"
+ " -b, --bitrate #[KMG][/#] target bitrate in bits/sec (0 for unlimited)\n"
+ " (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"
+#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"
+ " -l, --length #[KMG] length of buffer to read or write\n"
+ " (default %d KB for TCP, dynamic or %d for UDP)\n"
+ " --cport <port> bind to a specific client port (TCP and UDP, default: ephemeral port)\n"
+ " -P, --parallel # number of parallel client streams to run\n"
+ " -R, --reverse run in reverse mode (server sends, client receives)\n"
+ " --bidir run in bidirectional mode.\n"
+ " Client and server send and receive data.\n"
+ " -w, --window #[KMG] set send/receive socket buffer sizes\n"
+ " (indirectly sets TCP window size)\n"
+
+#if defined(HAVE_TCP_CONGESTION)
+ " -C, --congestion <algo> set TCP congestion control algorithm (Linux and FreeBSD only)\n"
+#endif /* HAVE_TCP_CONGESTION */
+ " -M, --set-mss # set TCP/SCTP maximum segment size (MTU - 40 bytes)\n"
+ " -N, --no-delay set TCP/SCTP no delay, disabling Nagle's Algorithm\n"
+ " -4, --version4 only use IPv4\n"
+ " -6, --version6 only use IPv6\n"
+ " -S, --tos N set the IP type of service, 0-255.\n"
+ " The usual prefixes for octal and hex can be used,\n"
+ " i.e. 52, 064 and 0x34 all specify the same value.\n"
+
+ " --dscp N or --dscp val set the IP dscp value, either 0-63 or symbolic.\n"
+ " Numeric values can be specified in decimal,\n"
+ " octal and hex (see --tos above).\n"
+#if defined(HAVE_FLOWLABEL)
+ " -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"
+ " -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"
+ " --repeating-payload use repeating pattern in payload, instead of\n"
+ " randomized payload (like in iperf2)\n"
+#if defined(HAVE_DONT_FRAGMENT)
+ " --dont-fragment set IPv4 Don't Fragment flag\n"
+#endif /* HAVE_DONT_FRAGMENT */
+#if defined(HAVE_SSL)
+ " --username username for authentication\n"
+ " --rsa-public-key-path path to the RSA public key used to encrypt\n"
+ " authentication credentials\n"
+#endif //HAVESSL
+
+#ifdef NOT_YET_SUPPORTED /* still working on these */
+#endif
+
+ "\n"
+ "[KMG] indicates options that support a K/M/G suffix for kilo-, mega-, or giga-\n"
+ "\n"
+#ifdef PACKAGE_URL
+ "iperf3 homepage at: " PACKAGE_URL "\n"
+#endif /* PACKAGE_URL */
+#ifdef PACKAGE_BUGREPORT
+ "Report bugs to: " PACKAGE_BUGREPORT "\n"
+#endif /* PACKAGE_BUGREPORT */
+ ;
+
+#ifdef OBSOLETE /* from old iperf: no longer supported. Add some of these back someday */
+ "-d, --dualtest Do a bidirectional test simultaneously\n"
+ "-L, --listenport # port to receive bidirectional tests back on\n"
+ "-I, --stdin input the data to be transmitted from stdin\n"
+ "-F, --fileinput <name> input the data to be transmitted from a file\n"
+ "-r, --tradeoff Do a bidirectional test individually\n"
+ "-T, --ttl # time-to-live, for multicast (default 1)\n"
+ "-x, --reportexclude [CDMSV] exclude C(connection) D(data) M(multicast) S(settings) V(server) reports\n"
+ "-y, --reportstyle C report as a Comma-Separated Values\n"
+#endif
+
+const char version[] = PACKAGE_STRING;
+
+/* -------------------------------------------------------------------
+ * settings
+ * ------------------------------------------------------------------- */
+
+const char seperator_line[] =
+"------------------------------------------------------------\n";
+
+const char server_port[] =
+"Server listening on %s port %d\n";
+
+const char client_port[] =
+"Client connecting to %s, %s port %d\n";
+
+const char bind_address[] =
+"Binding to local address %s\n";
+
+const char bind_dev[] =
+"Binding to local network device %s\n";
+
+const char bind_port[] =
+"Binding to local port %s\n";
+
+const char multicast_ttl[] =
+"Setting multicast TTL to %d\n";
+
+const char join_multicast[] =
+"Joining multicast group %s\n";
+
+const char client_datagram_size[] =
+"Sending %d byte datagrams\n";
+
+const char server_datagram_size[] =
+"Receiving %d byte datagrams\n";
+
+const char tcp_window_size[] =
+"TCP window size";
+
+const char udp_buffer_size[] =
+"UDP buffer size";
+
+const char window_default[] =
+"(default)";
+
+const char wait_server_threads[] =
+"Waiting for server threads to complete. Interrupt again to force quit.\n";
+
+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";
+
+const char test_start_blocks[] =
+"Starting Test: protocol: %s, %d streams, %d byte blocks, omitting %d seconds, %d blocks to send, tos %d\n";
+
+
+/* -------------------------------------------------------------------
+ * reports
+ * ------------------------------------------------------------------- */
+
+const char report_time[] =
+"Time: %s\n";
+
+const char report_connecting[] =
+"Connecting to host %s, port %d\n";
+
+const char report_authentication_succeeded[] =
+"Authentication succeeded for user '%s' ts %ld\n";
+
+const char report_authentication_failed[] =
+"Authentication failed with return code %d for user '%s' ts %ld\n";
+
+const char report_reverse[] =
+"Reverse mode, remote host %s is sending\n";
+
+const char report_accepted[] =
+"Accepted connection from %s, port %d\n";
+
+const char report_cookie[] =
+" Cookie: %s\n";
+
+const char report_connected[] =
+"[%3d] local %s port %d connected to %s port %d\n";
+
+const char report_window[] =
+"TCP window size: %s\n";
+
+const char report_autotune[] =
+"Using TCP Autotuning\n";
+
+const char report_omit_done[] =
+"Finished omit period, starting real test\n";
+
+const char report_diskfile[] =
+" Sent %s / %s (%d%%) of %s\n";
+
+const char report_done[] =
+"iperf Done.\n";
+
+const char report_read_lengths[] =
+"[%3d] Read lengths occurring in more than 5%% of reads:\n";
+
+const char report_read_length_times[] =
+"[%3d] %5d bytes read %5d times (%.3g%%)\n";
+
+const char report_bw_header[] =
+"[ ID] Interval Transfer Bitrate\n";
+
+const char report_bw_header_bidir[] =
+"[ ID][Role] Interval Transfer Bitrate\n";
+
+const char report_bw_retrans_header[] =
+"[ ID] Interval Transfer Bitrate Retr\n";
+
+const char report_bw_retrans_header_bidir[] =
+"[ ID][Role] Interval Transfer Bitrate Retr\n";
+
+const char report_bw_retrans_cwnd_header[] =
+"[ ID] Interval Transfer Bitrate Retr Cwnd\n";
+
+const char report_bw_retrans_cwnd_header_bidir[] =
+"[ ID][Role] Interval Transfer Bitrate Retr Cwnd\n";
+
+const char report_bw_udp_header[] =
+"[ ID] Interval Transfer Bitrate Jitter Lost/Total Datagrams\n";
+
+const char report_bw_udp_header_bidir[] =
+"[ ID][Role] Interval Transfer Bitrate Jitter Lost/Total Datagrams\n";
+
+const char report_bw_udp_sender_header[] =
+"[ ID] Interval Transfer Bitrate Total Datagrams\n";
+
+const char report_bw_udp_sender_header_bidir[] =
+"[ ID][Role] Interval Transfer Bitrate Total Datagrams\n";
+
+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";
+
+const char report_bw_retrans_cwnd_format[] =
+"[%3d]%s %6.2f-%-6.2f sec %ss %ss/sec %3u %ss %s\n";
+
+const char report_bw_udp_format[] =
+"[%3d]%s %6.2f-%-6.2f sec %ss %ss/sec %5.3f ms %" PRIu64 "/%" PRIu64 " (%.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/%" PRIu64 " %s\n";
+
+const char report_bw_udp_sender_format[] =
+"[%3d]%s %6.2f-%-6.2f sec %ss %ss/sec %s %" PRIu64 " %s\n";
+
+const char report_summary[] =
+"Test Complete. Summary Results:\n";
+
+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";
+
+const char report_sum_bw_udp_format[] =
+"[SUM]%s %6.2f-%-6.2f sec %ss %ss/sec %5.3f ms %" PRIu64 "/%" PRIu64 " (%.2g%%) %s\n";
+
+const char report_sum_bw_udp_sender_format[] =
+"[SUM]%s %6.2f-%-6.2f sec %ss %ss/sec %s %" PRIu64 " %s\n";
+
+const char report_omitted[] = "(omitted)";
+
+const char report_bw_separator[] =
+"- - - - - - - - - - - - - - - - - - - - - - - - -\n";
+
+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";
+
+const char report_peer[] =
+"[%3d] local %s port %u connected with %s port %u\n";
+
+const char report_mss_unsupported[] =
+"[%3d] MSS and MTU size unknown (TCP_MAXSEG not supported by OS?)\n";
+
+const char report_mss[] =
+"[%3d] MSS size %d bytes (MTU %d bytes, %s)\n";
+
+const char report_datagrams[] =
+"[%3d] Sent %d datagrams\n";
+
+const char report_sum_datagrams[] =
+"[SUM] Sent %d datagrams\n";
+
+const char server_reporting[] =
+"[%3d] Server Report:\n";
+
+const char reportCSV_peer[] =
+"%s,%u,%s,%u";
+
+const char report_cpu[] =
+"CPU Utilization: %s/%s %.1f%% (%.1f%%u/%.1f%%s), %s/%s %.1f%% (%.1f%%u/%.1f%%s)\n";
+
+const char report_local[] = "local";
+const char report_remote[] = "remote";
+const char report_sender[] = "sender";
+const char report_receiver[] = "receiver";
+const char report_sender_not_available_format[] = "[%3d] (sender statistics not available)\n";
+const char report_sender_not_available_summary_format[] = "[%3s] (sender statistics not available)\n";
+const char report_receiver_not_available_format[] = "[%3d] (receiver statistics not available)\n";
+const char report_receiver_not_available_summary_format[] = "[%3s] (receiver statistics not available)\n";
+
+#if defined(linux)
+const char report_tcpInfo[] =
+"event=TCP_Info CWND=%u SND_SSTHRESH=%u RCV_SSTHRESH=%u UNACKED=%u SACK=%u LOST=%u RETRANS=%u FACK=%u RTT=%u REORDERING=%u\n";
+#endif
+#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
+const char report_tcpInfo[] =
+"event=TCP_Info CWND=%u RCV_WIND=%u SND_SSTHRESH=%u RTT=%u\n";
+#endif
+
+
+#ifdef HAVE_QUAD_SUPPORT
+#ifdef HAVE_PRINTF_QD
+const char reportCSV_bw_format[] =
+"%s,%s,%d,%.1f-%.1f,%qd,%qd\n";
+
+const char reportCSV_bw_udp_format[] =
+"%s,%s,%d,%.1f-%.1f,%qd,%qd,%.3f,%d,%d,%.3f,%d\n";
+#else // HAVE_PRINTF_QD
+const char reportCSV_bw_format[] =
+"%s,%s,%d,%.1f-%.1f,%lld,%lld\n";
+
+const char reportCSV_bw_udp_format[] =
+"%s,%s,%d,%.1f-%.1f,%lld,%lld,%.3f,%d,%d,%.3f,%d\n";
+#endif // HAVE_PRINTF_QD
+#else // HAVE_QUAD_SUPPORT
+#ifdef WIN32
+const char reportCSV_bw_format[] =
+"%s,%s,%d,%.1f-%.1f,%I64d,%I64d\n";
+
+const char reportCSV_bw_udp_format[] =
+"%s,%s,%d,%.1f-%.1f,%I64d,%I64d,%.3f,%d,%d,%.3f,%d\n";
+#else
+const char reportCSV_bw_format[] =
+"%s,%s,%d,%.1f-%.1f,%d,%d\n";
+
+const char reportCSV_bw_udp_format[] =
+"%s,%s,%d,%.1f-%.1f,%d,%d,%.3f,%d,%d,%.3f,%d\n";
+#endif //WIN32
+#endif //HAVE_QUAD_SUPPORT
+/* -------------------------------------------------------------------
+ * warnings
+ * ------------------------------------------------------------------- */
+
+const char warn_window_requested[] =
+" (WARNING: requested %s)";
+
+const char warn_window_small[] =
+"WARNING: TCP window size set to %d bytes. A small window size\n\
+will give poor performance. See the Iperf documentation.\n";
+
+const char warn_delay_large[] =
+"WARNING: delay too large, reducing from %.1f to 1.0 seconds.\n";
+
+const char warn_no_pathmtu[] =
+"WARNING: Path MTU Discovery may not be enabled.\n";
+
+const char warn_no_ack[]=
+"[%3d] WARNING: did not receive ack of last datagram after %d tries.\n";
+
+const char warn_ack_failed[]=
+"[%3d] WARNING: ack of last datagram failed after %d tries.\n";
+
+const char warn_fileopen_failed[]=
+"WARNING: Unable to open file stream for transfer\n\
+Using default data stream. \n";
+
+const char unable_to_change_win[]=
+"WARNING: Unable to change the window size\n";
+
+const char opt_estimate[]=
+"Optimal Estimate\n";
+
+const char report_interval_small[] =
+"WARNING: interval too small, increasing from %3.2f to 0.5 seconds.\n";
+
+const char warn_invalid_server_option[] =
+"WARNING: option -%c is not valid for server mode\n";
+
+const char warn_invalid_client_option[] =
+"WARNING: option -%c is not valid for client mode\n";
+
+const char warn_invalid_compatibility_option[] =
+"WARNING: option -%c is not valid in compatibility mode\n";
+
+const char warn_implied_udp[] =
+"WARNING: option -%c implies udp testing\n";
+
+const char warn_implied_compatibility[] =
+"WARNING: option -%c has implied compatibility mode\n";
+
+const char warn_buffer_too_small[] =
+"WARNING: the UDP buffer was increased to %d for proper operation\n";
+
+const char warn_invalid_single_threaded[] =
+"WARNING: option -%c is not valid in single threaded versions\n";
+
+const char warn_invalid_report_style[] =
+"WARNING: unknown reporting style \"%s\", switching to default\n";
+
+const char warn_invalid_report[] =
+"WARNING: unknown reporting type \"%c\", ignored\n valid options are:\n\t exclude: C(connection) D(data) M(multicast) S(settings) V(server) report\n\n";
+
+#ifdef __cplusplus
+} /* end extern "C" */
+#endif
diff --git a/src/iperf_locale.h b/src/iperf_locale.h
new file mode 100644
index 0000000..bc9c96c
--- /dev/null
+++ b/src/iperf_locale.h
@@ -0,0 +1,134 @@
+/*
+ * iperf, Copyright (c) 2014-2020, The Regents of the University of
+ * California, through Lawrence Berkeley National Laboratory (subject
+ * to receipt of any required approvals from the U.S. Dept. of
+ * Energy). All rights reserved.
+ *
+ * If you have questions about your rights to use or distribute this
+ * software, please contact Berkeley Lab's Technology Transfer
+ * Department at TTD@lbl.gov.
+ *
+ * NOTICE. This software is owned by the U.S. Department of Energy.
+ * As such, the U.S. Government has been granted for itself and others
+ * acting on its behalf a paid-up, nonexclusive, irrevocable,
+ * worldwide license in the Software to reproduce, prepare derivative
+ * works, and perform publicly and display publicly. Beginning five
+ * (5) years after the date permission to assert copyright is obtained
+ * from the U.S. Department of Energy, and subject to any subsequent
+ * five (5) year renewals, the U.S. Government is granted for itself
+ * and others acting on its behalf a paid-up, nonexclusive,
+ * irrevocable, worldwide license in the Software to reproduce,
+ * prepare derivative works, distribute copies to the public, perform
+ * publicly and display publicly, and to permit others to do so.
+ *
+ * This code is distributed under a BSD style license, see the LICENSE
+ * file for complete information.
+ */
+#ifndef IPERF_LOCALE_H
+#define IPERF_LOCALE_H
+
+extern const char usage_shortstr[];
+extern const char usage_longstr[];
+extern const char version[];
+
+extern const char seperator_line[];
+
+extern const char server_port[] ;
+extern const char client_port[] ;
+extern const char bind_address[] ;
+extern const char bind_dev[] ;
+extern const char multicast_ttl[] ;
+extern const char join_multicast[] ;
+extern const char client_datagram_size[] ;
+extern const char server_datagram_size[] ;
+extern const char tcp_window_size[] ;
+extern const char udp_buffer_size[] ;
+extern const char window_default[] ;
+extern const char wait_server_threads[] ;
+extern const char test_start_time[];
+extern const char test_start_bytes[];
+extern const char test_start_blocks[];
+
+extern const char report_time[] ;
+extern const char report_connecting[] ;
+extern const char report_reverse[] ;
+extern const char report_accepted[] ;
+extern const char report_cookie[] ;
+extern const char report_connected[] ;
+extern const char report_authentication_succeeded[] ;
+extern const char report_authentication_failed[] ;
+extern const char report_window[] ;
+extern const char report_autotune[] ;
+extern const char report_omit_done[] ;
+extern const char report_diskfile[] ;
+extern const char report_done[] ;
+extern const char report_read_lengths[] ;
+extern const char report_read_length_times[] ;
+extern const char report_bw_header[] ;
+extern const char report_bw_header_bidir[] ;
+extern const char report_bw_retrans_header[] ;
+extern const char report_bw_retrans_header_bidir[] ;
+extern const char report_bw_retrans_cwnd_header[] ;
+extern const char report_bw_retrans_cwnd_header_bidir[] ;
+extern const char report_bw_udp_header[] ;
+extern const char report_bw_udp_header_bidir[] ;
+extern const char report_bw_udp_sender_header[] ;
+extern const char report_bw_udp_sender_header_bidir[] ;
+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[] ;
+extern const char report_sum_bw_retrans_format[] ;
+extern const char report_sum_bw_udp_format[] ;
+extern const char report_sum_bw_udp_sender_format[] ;
+extern const char report_omitted[] ;
+extern const char report_bw_separator[] ;
+extern const char report_outoforder[] ;
+extern const char report_sum_outoforder[] ;
+extern const char report_peer[] ;
+extern const char report_mss_unsupported[] ;
+extern const char report_mss[] ;
+extern const char report_datagrams[] ;
+extern const char report_sum_datagrams[] ;
+extern const char server_reporting[] ;
+extern const char reportCSV_peer[] ;
+
+extern const char report_cpu[] ;
+extern const char report_local[] ;
+extern const char report_remote[] ;
+extern const char report_sender[] ;
+extern const char report_receiver[] ;
+extern const char report_sender_not_available_format[];
+extern const char report_sender_not_available_summary_format[];
+extern const char report_receiver_not_available_format[];
+extern const char report_receiver_not_available_summary_format[];
+
+extern const char report_tcpInfo[] ;
+extern const char report_tcpInfo[] ;
+
+
+extern const char warn_window_requested[] ;
+extern const char warn_window_small[] ;
+extern const char warn_delay_large[] ;
+extern const char warn_no_pathmtu[] ;
+extern const char warn_no_ack[];
+extern const char warn_ack_failed[];
+extern const char warn_fileopen_failed[];
+extern const char unable_to_change_win[];
+extern const char opt_estimate[];
+extern const char report_interval_small[] ;
+extern const char warn_invalid_server_option[] ;
+extern const char warn_invalid_client_option[] ;
+extern const char warn_invalid_compatibility_option[] ;
+extern const char warn_implied_udp[] ;
+extern const char warn_implied_compatibility[] ;
+extern const char warn_buffer_too_small[] ;
+extern const char warn_invalid_single_threaded[] ;
+extern const char warn_invalid_report_style[] ;
+extern const char warn_invalid_report[] ;
+
+#endif
diff --git a/src/iperf_sctp.c b/src/iperf_sctp.c
new file mode 100644
index 0000000..1040832
--- /dev/null
+++ b/src/iperf_sctp.c
@@ -0,0 +1,736 @@
+/*
+ * iperf, Copyright (c) 2014-2019, 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.
+ *
+ * If you have questions about your rights to use or distribute this
+ * software, please contact Berkeley Lab's Technology Transfer
+ * Department at TTD@lbl.gov.
+ *
+ * NOTICE. This software is owned by the U.S. Department of Energy.
+ * As such, the U.S. Government has been granted for itself and others
+ * acting on its behalf a paid-up, nonexclusive, irrevocable,
+ * worldwide license in the Software to reproduce, prepare derivative
+ * works, and perform publicly and display publicly. Beginning five
+ * (5) years after the date permission to assert copyright is obtained
+ * from the U.S. Department of Energy, and subject to any subsequent
+ * five (5) year renewals, the U.S. Government is granted for itself
+ * and others acting on its behalf a paid-up, nonexclusive,
+ * irrevocable, worldwide license in the Software to reproduce,
+ * prepare derivative works, distribute copies to the public, perform
+ * publicly and display publicly, and to permit others to do so.
+ *
+ * This code is distributed under a BSD style license, see the LICENSE
+ * file for complete information.
+ */
+#include "iperf_config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <sys/time.h>
+#include <sys/select.h>
+#include <limits.h>
+
+#ifdef HAVE_NETINET_SCTP_H
+#include <netinet/sctp.h>
+#endif /* HAVE_NETINET_SCTP_H */
+
+#include "iperf.h"
+#include "iperf_api.h"
+#include "iperf_sctp.h"
+#include "net.h"
+
+
+
+/* iperf_sctp_recv
+ *
+ * receives the data for SCTP
+ */
+int
+iperf_sctp_recv(struct iperf_stream *sp)
+{
+#if defined(HAVE_SCTP_H)
+ int r;
+
+ r = Nread(sp->socket, sp->buffer, sp->settings->blksize, Psctp);
+ 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;
+ }
+ else {
+ if (sp->test->debug)
+ printf("Late receive, state = %d\n", sp->test->state);
+ }
+
+ return r;
+#else
+ i_errno = IENOSCTP;
+ return -1;
+#endif /* HAVE_SCTP_H */
+}
+
+
+/* iperf_sctp_send
+ *
+ * sends the data for SCTP
+ */
+int
+iperf_sctp_send(struct iperf_stream *sp)
+{
+#if defined(HAVE_SCTP_H)
+ int r;
+
+ r = Nwrite(sp->socket, sp->buffer, sp->settings->blksize, Psctp);
+ if (r < 0)
+ return r;
+
+ sp->result->bytes_sent += r;
+ sp->result->bytes_sent_this_interval += r;
+
+ return r;
+#else
+ i_errno = IENOSCTP;
+ return -1;
+#endif /* HAVE_SCTP_H */
+}
+
+
+
+/* iperf_sctp_accept
+ *
+ * accept a new SCTP stream connection
+ */
+int
+iperf_sctp_accept(struct iperf_test * test)
+{
+#if defined(HAVE_SCTP_H)
+ int s;
+ signed char rbuf = ACCESS_DENIED;
+ char cookie[COOKIE_SIZE];
+ socklen_t len;
+ struct sockaddr_storage addr;
+
+ len = sizeof(addr);
+ s = accept(test->listener, (struct sockaddr *) &addr, &len);
+ if (s < 0) {
+ i_errno = IESTREAMCONNECT;
+ return -1;
+ }
+
+ if (Nread(s, cookie, COOKIE_SIZE, Psctp) < 0) {
+ i_errno = IERECVCOOKIE;
+ close(s);
+ return -1;
+ }
+
+ if (strncmp(test->cookie, cookie, COOKIE_SIZE) != 0) {
+ if (Nwrite(s, (char*) &rbuf, sizeof(rbuf), Psctp) < 0) {
+ i_errno = IESENDMESSAGE;
+ close(s);
+ return -1;
+ }
+ close(s);
+ }
+
+ return s;
+#else
+ i_errno = IENOSCTP;
+ return -1;
+#endif /* HAVE_SCTP_H */
+}
+
+
+/* iperf_sctp_listen
+ *
+ * start up a listener for SCTP stream connections
+ */
+int
+iperf_sctp_listen(struct iperf_test *test)
+{
+#if defined(HAVE_SCTP_H)
+ struct addrinfo hints, *res;
+ char portstr[6];
+ int s, opt, saved_errno;
+
+ close(test->listener);
+ test->listener = -1;
+
+ snprintf(portstr, 6, "%d", test->server_port);
+ memset(&hints, 0, sizeof(hints));
+ /*
+ * 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().
+ */
+ if (test->settings->domain == AF_UNSPEC && !test->bind_address) {
+ hints.ai_family = AF_INET6;
+ } else {
+ hints.ai_family = test->settings->domain;
+ }
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = AI_PASSIVE;
+ if ((gerror = getaddrinfo(test->bind_address, portstr, &hints, &res)) != 0) {
+ i_errno = IESTREAMLISTEN;
+ return -1;
+ }
+
+ if ((s = socket(res->ai_family, SOCK_STREAM, IPPROTO_SCTP)) < 0) {
+ freeaddrinfo(res);
+ i_errno = IESTREAMLISTEN;
+ return -1;
+ }
+
+ if ((opt = test->settings->socket_bufsize)) {
+ int saved_errno;
+ if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt)) < 0) {
+ saved_errno = errno;
+ close(s);
+ freeaddrinfo(res);
+ errno = saved_errno;
+ i_errno = IESETBUF;
+ return -1;
+ }
+ if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, &opt, sizeof(opt)) < 0) {
+ saved_errno = errno;
+ close(s);
+ freeaddrinfo(res);
+ errno = saved_errno;
+ i_errno = IESETBUF;
+ return -1;
+ }
+ }
+
+ if (test->bind_dev) {
+#if defined(SO_BINDTODEVICE)
+ if (setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE,
+ test->bind_dev, IFNAMSIZ) < 0)
+#endif // SO_BINDTODEVICE
+ {
+ saved_errno = errno;
+ close(s);
+ freeaddrinfo(res);
+ i_errno = IEBINDDEV;
+ errno = saved_errno;
+ return -1;
+ }
+ }
+
+#if defined(IPV6_V6ONLY) && !defined(__OpenBSD__)
+ if (res->ai_family == AF_INET6 && (test->settings->domain == AF_UNSPEC ||
+ test->settings->domain == AF_INET6)) {
+ if (test->settings->domain == AF_UNSPEC)
+ opt = 0;
+ else
+ opt = 1;
+ if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY,
+ (char *) &opt, sizeof(opt)) < 0) {
+ saved_errno = errno;
+ close(s);
+ freeaddrinfo(res);
+ errno = saved_errno;
+ i_errno = IEPROTOCOL;
+ return -1;
+ }
+ }
+#endif /* IPV6_V6ONLY */
+
+ opt = 1;
+ if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) {
+ saved_errno = errno;
+ close(s);
+ freeaddrinfo(res);
+ errno = saved_errno;
+ i_errno = IEREUSEADDR;
+ return -1;
+ }
+
+ /* servers must call sctp_bindx() _instead_ of bind() */
+ if (!TAILQ_EMPTY(&test->xbind_addrs)) {
+ if (iperf_sctp_bindx(test, s, IPERF_SCTP_SERVER)) {
+ close(s);
+ freeaddrinfo(res);
+ return -1;
+ }
+ } else
+ if (bind(s, (struct sockaddr *) res->ai_addr, res->ai_addrlen) < 0) {
+ saved_errno = errno;
+ close(s);
+ freeaddrinfo(res);
+ errno = saved_errno;
+ i_errno = IESTREAMLISTEN;
+ return -1;
+ }
+
+ freeaddrinfo(res);
+
+ if (listen(s, INT_MAX) < 0) {
+ i_errno = IESTREAMLISTEN;
+ return -1;
+ }
+
+ test->listener = s;
+
+ return s;
+#else
+ i_errno = IENOSCTP;
+ return -1;
+#endif /* HAVE_SCTP_H */
+}
+
+
+/* iperf_sctp_connect
+ *
+ * connect to a SCTP stream listener
+ */
+int
+iperf_sctp_connect(struct iperf_test *test)
+{
+#if defined(HAVE_SCTP_H)
+ int s, opt, saved_errno;
+ char portstr[6];
+ struct addrinfo hints, *local_res = NULL, *server_res = NULL;
+
+ if (test->bind_address) {
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = test->settings->domain;
+ hints.ai_socktype = SOCK_STREAM;
+ if ((gerror = getaddrinfo(test->bind_address, NULL, &hints, &local_res)) != 0) {
+ i_errno = IESTREAMCONNECT;
+ return -1;
+ }
+ }
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = test->settings->domain;
+ hints.ai_socktype = SOCK_STREAM;
+ snprintf(portstr, sizeof(portstr), "%d", test->server_port);
+ if ((gerror = getaddrinfo(test->server_hostname, portstr, &hints, &server_res)) != 0) {
+ if (test->bind_address)
+ freeaddrinfo(local_res);
+ i_errno = IESTREAMCONNECT;
+ return -1;
+ }
+
+ s = socket(server_res->ai_family, SOCK_STREAM, IPPROTO_SCTP);
+ if (s < 0) {
+ freeaddrinfo(local_res);
+ freeaddrinfo(server_res);
+ i_errno = IESTREAMCONNECT;
+ return -1;
+ }
+
+ if ((opt = test->settings->socket_bufsize)) {
+ int saved_errno;
+ if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt)) < 0) {
+ saved_errno = errno;
+ close(s);
+ freeaddrinfo(server_res);
+ errno = saved_errno;
+ i_errno = IESETBUF;
+ return -1;
+ }
+ if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, &opt, sizeof(opt)) < 0) {
+ saved_errno = errno;
+ close(s);
+ freeaddrinfo(server_res);
+ errno = saved_errno;
+ i_errno = IESETBUF;
+ return -1;
+ }
+ }
+
+ if (test->bind_dev) {
+#if defined(SO_BINDTODEVICE)
+ if (setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE,
+ test->bind_dev, IFNAMSIZ) < 0)
+#endif // SO_BINDTODEVICE
+ {
+ saved_errno = errno;
+ close(s);
+ freeaddrinfo(local_res);
+ freeaddrinfo(server_res);
+ i_errno = IEBINDDEV;
+ errno = saved_errno;
+ return -1;
+ }
+ }
+
+ /*
+ * Various ways to bind the local end of the connection.
+ * 1. --bind (with or without --cport).
+ */
+ if (test->bind_address) {
+ struct sockaddr_in *lcladdr;
+ lcladdr = (struct sockaddr_in *)local_res->ai_addr;
+ lcladdr->sin_port = htons(test->bind_port);
+
+ if (bind(s, (struct sockaddr *) local_res->ai_addr, local_res->ai_addrlen) < 0) {
+ saved_errno = errno;
+ close(s);
+ freeaddrinfo(local_res);
+ freeaddrinfo(server_res);
+ errno = saved_errno;
+ i_errno = IESTREAMCONNECT;
+ return -1;
+ }
+ freeaddrinfo(local_res);
+ }
+ /* --cport, no --bind */
+ else if (test->bind_port) {
+ size_t addrlen;
+ struct sockaddr_storage lcl;
+
+ /* IPv4 */
+ if (server_res->ai_family == AF_INET) {
+ struct sockaddr_in *lcladdr = (struct sockaddr_in *) &lcl;
+ lcladdr->sin_family = AF_INET;
+ lcladdr->sin_port = htons(test->bind_port);
+ lcladdr->sin_addr.s_addr = INADDR_ANY;
+ addrlen = sizeof(struct sockaddr_in);
+ }
+ /* IPv6 */
+ else if (server_res->ai_family == AF_INET6) {
+ struct sockaddr_in6 *lcladdr = (struct sockaddr_in6 *) &lcl;
+ lcladdr->sin6_family = AF_INET6;
+ lcladdr->sin6_port = htons(test->bind_port);
+ lcladdr->sin6_addr = in6addr_any;
+ addrlen = sizeof(struct sockaddr_in6);
+ }
+ /* Unknown protocol */
+ else {
+ saved_errno = errno;
+ close(s);
+ freeaddrinfo(server_res);
+ errno = saved_errno;
+ i_errno = IEPROTOCOL;
+ return -1;
+ }
+
+ if (bind(s, (struct sockaddr *) &lcl, addrlen) < 0) {
+ saved_errno = errno;
+ close(s);
+ freeaddrinfo(server_res);
+ errno = saved_errno;
+ i_errno = IESTREAMCONNECT;
+ return -1;
+ }
+ }
+
+ if (test->no_delay != 0) {
+ opt = 1;
+ if (setsockopt(s, IPPROTO_SCTP, SCTP_NODELAY, &opt, sizeof(opt)) < 0) {
+ saved_errno = errno;
+ close(s);
+ freeaddrinfo(server_res);
+ errno = saved_errno;
+ i_errno = IESETNODELAY;
+ return -1;
+ }
+ }
+
+ if ((test->settings->mss >= 512 && test->settings->mss <= 131072)) {
+
+ /*
+ * Some platforms use a struct sctp_assoc_value as the
+ * argument to SCTP_MAXSEG. Other (older API implementations)
+ * take an int. FreeBSD 10 and CentOS 6 support SCTP_MAXSEG,
+ * but OpenSolaris 11 doesn't.
+ */
+#ifdef HAVE_STRUCT_SCTP_ASSOC_VALUE
+ struct sctp_assoc_value av;
+
+ /*
+ * Some platforms support SCTP_FUTURE_ASSOC, others need to
+ * (equivalently) do 0 here. FreeBSD 10 is an example of the
+ * former, CentOS 6 Linux is an example of the latter.
+ */
+#ifdef SCTP_FUTURE_ASSOC
+ av.assoc_id = SCTP_FUTURE_ASSOC;
+#else
+ av.assoc_id = 0;
+#endif
+ av.assoc_value = test->settings->mss;
+
+ if (setsockopt(s, IPPROTO_SCTP, SCTP_MAXSEG, &av, sizeof(av)) < 0) {
+ saved_errno = errno;
+ close(s);
+ freeaddrinfo(server_res);
+ errno = saved_errno;
+ i_errno = IESETMSS;
+ return -1;
+ }
+#else
+ opt = test->settings->mss;
+
+ /*
+ * Solaris might not support this option. If it doesn't work,
+ * ignore the error (at least for now).
+ */
+ if (setsockopt(s, IPPROTO_SCTP, SCTP_MAXSEG, &opt, sizeof(opt)) < 0 &&
+ errno != ENOPROTOOPT) {
+ saved_errno = errno;
+ close(s);
+ freeaddrinfo(server_res);
+ errno = saved_errno;
+ i_errno = IESETMSS;
+ return -1;
+ }
+#endif /* HAVE_STRUCT_SCTP_ASSOC_VALUE */
+ }
+
+ if (test->settings->num_ostreams > 0) {
+ struct sctp_initmsg initmsg;
+
+ memset(&initmsg, 0, sizeof(struct sctp_initmsg));
+ initmsg.sinit_num_ostreams = test->settings->num_ostreams;
+
+ if (setsockopt(s, IPPROTO_SCTP, SCTP_INITMSG, &initmsg, sizeof(struct sctp_initmsg)) < 0) {
+ saved_errno = errno;
+ close(s);
+ freeaddrinfo(server_res);
+ errno = saved_errno;
+ i_errno = IESETSCTPNSTREAM;
+ return -1;
+ }
+ }
+
+ /* clients must call bind() followed by sctp_bindx() before connect() */
+ if (!TAILQ_EMPTY(&test->xbind_addrs)) {
+ if (iperf_sctp_bindx(test, s, IPERF_SCTP_CLIENT)) {
+ freeaddrinfo(server_res);
+ close(s);
+ return -1;
+ }
+ }
+
+ /* TODO support sctp_connectx() to avoid heartbeating. */
+ if (connect(s, (struct sockaddr *) server_res->ai_addr, server_res->ai_addrlen) < 0 && errno != EINPROGRESS) {
+ saved_errno = errno;
+ close(s);
+ freeaddrinfo(server_res);
+ errno = saved_errno;
+ i_errno = IESTREAMCONNECT;
+ return -1;
+ }
+
+ /* Send cookie for verification */
+ if (Nwrite(s, test->cookie, COOKIE_SIZE, Psctp) < 0) {
+ saved_errno = errno;
+ close(s);
+ freeaddrinfo(server_res);
+ errno = saved_errno;
+ i_errno = IESENDCOOKIE;
+ return -1;
+ }
+
+ /*
+ * We want to allow fragmentation. But there's at least one
+ * implementation (Solaris) that doesn't support this option,
+ * even though it defines SCTP_DISABLE_FRAGMENTS. So we have to
+ * try setting the option and ignore the error, if it doesn't
+ * work.
+ */
+ opt = 0;
+ if (setsockopt(s, IPPROTO_SCTP, SCTP_DISABLE_FRAGMENTS, &opt, sizeof(opt)) < 0 &&
+ errno != ENOPROTOOPT) {
+ saved_errno = errno;
+ close(s);
+ freeaddrinfo(server_res);
+ errno = saved_errno;
+ i_errno = IESETSCTPDISABLEFRAG;
+ return -1;
+ }
+
+ freeaddrinfo(server_res);
+ return s;
+#else
+ i_errno = IENOSCTP;
+ return -1;
+#endif /* HAVE_SCTP_H */
+}
+
+
+
+int
+iperf_sctp_init(struct iperf_test *test)
+{
+#if defined(HAVE_SCTP_H)
+ return 0;
+#else
+ i_errno = IENOSCTP;
+ return -1;
+#endif /* HAVE_SCTP_H */
+}
+
+
+
+/* iperf_sctp_bindx
+ *
+ * handle binding to multiple endpoints (-X parameters)
+ */
+int
+iperf_sctp_bindx(struct iperf_test *test, int s, int is_server)
+{
+#if defined(HAVE_SCTP_H)
+ struct addrinfo hints;
+ char portstr[6];
+ char *servname;
+ struct addrinfo *ai, *ai0;
+ struct sockaddr *xaddrs;
+ struct xbind_entry *xbe, *xbe0;
+ char *bp;
+ size_t xaddrlen;
+ int nxaddrs;
+ int retval;
+ int domain;
+ int saved_errno;
+
+ domain = test->settings->domain;
+ xbe0 = NULL;
+ retval = 0;
+
+ if (TAILQ_EMPTY(&test->xbind_addrs))
+ return retval; /* nothing to do */
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = (domain == AF_UNSPEC ? AF_INET6 : domain);
+ hints.ai_socktype = SOCK_STREAM;
+ servname = NULL;
+ if (is_server) {
+ hints.ai_flags |= AI_PASSIVE;
+ snprintf(portstr, 6, "%d", test->server_port);
+ servname = portstr;
+ }
+
+ /* client: must pop first -X address and call bind().
+ * sctp_bindx() must see the ephemeral port chosen by bind().
+ * we deliberately ignore the -B argument in this case.
+ */
+ if (!is_server) {
+ struct sockaddr *sa;
+ struct sockaddr_in *sin;
+ struct sockaddr_in6 *sin6;
+ int eport;
+
+ xbe0 = TAILQ_FIRST(&test->xbind_addrs);
+ TAILQ_REMOVE(&test->xbind_addrs, xbe0, link);
+
+ if ((gerror = getaddrinfo(xbe0->name, servname, &hints, &xbe0->ai)) != 0) {
+ i_errno = IESETSCTPBINDX;
+ retval = -1;
+ goto out;
+ }
+
+ ai = xbe0->ai;
+ if (domain != AF_UNSPEC && domain != ai->ai_family) {
+ i_errno = IESETSCTPBINDX;
+ retval = -1;
+ goto out;
+ }
+ if (bind(s, (struct sockaddr *)ai->ai_addr, ai->ai_addrlen) < 0) {
+ i_errno = IESETSCTPBINDX;
+ retval = -1;
+ goto out;
+ }
+
+ /* if only one -X argument, nothing more to do */
+ if (TAILQ_EMPTY(&test->xbind_addrs))
+ goto out;
+
+ sa = (struct sockaddr *)ai->ai_addr;
+ if (sa->sa_family == AF_INET) {
+ sin = (struct sockaddr_in *)ai->ai_addr;
+ eport = sin->sin_port;
+ } else if (sa->sa_family == AF_INET6) {
+ sin6 = (struct sockaddr_in6 *)ai->ai_addr;
+ eport = sin6->sin6_port;
+ } else {
+ i_errno = IESETSCTPBINDX;
+ retval = -1;
+ goto out;
+ }
+ snprintf(portstr, 6, "%d", ntohs(eport));
+ servname = portstr;
+ }
+
+ /* pass 1: resolve and compute lengths. */
+ nxaddrs = 0;
+ xaddrlen = 0;
+ TAILQ_FOREACH(xbe, &test->xbind_addrs, link) {
+ if (xbe->ai != NULL)
+ freeaddrinfo(xbe->ai);
+ if ((gerror = getaddrinfo(xbe->name, servname, &hints, &xbe->ai)) != 0) {
+ i_errno = IESETSCTPBINDX;
+ retval = -1;
+ goto out;
+ }
+ ai0 = xbe->ai;
+ for (ai = ai0; ai; ai = ai->ai_next) {
+ if (domain != AF_UNSPEC && domain != ai->ai_family)
+ continue;
+ xaddrlen += ai->ai_addrlen;
+ ++nxaddrs;
+ }
+ }
+
+ /* pass 2: copy into flat buffer. */
+ xaddrs = (struct sockaddr *)malloc(xaddrlen);
+ if (!xaddrs) {
+ i_errno = IESETSCTPBINDX;
+ retval = -1;
+ goto out;
+ }
+ bp = (char *)xaddrs;
+ TAILQ_FOREACH(xbe, &test->xbind_addrs, link) {
+ ai0 = xbe->ai;
+ for (ai = ai0; ai; ai = ai->ai_next) {
+ if (domain != AF_UNSPEC && domain != ai->ai_family)
+ continue;
+ memcpy(bp, ai->ai_addr, ai->ai_addrlen);
+ bp += ai->ai_addrlen;
+ }
+ }
+
+ if (sctp_bindx(s, xaddrs, nxaddrs, SCTP_BINDX_ADD_ADDR) == -1) {
+ saved_errno = errno;
+ close(s);
+ free(xaddrs);
+ errno = saved_errno;
+ i_errno = IESETSCTPBINDX;
+ retval = -1;
+ goto out;
+ }
+
+ free(xaddrs);
+ retval = 0;
+
+out:
+ /* client: put head node back. */
+ if (!is_server && xbe0)
+ TAILQ_INSERT_HEAD(&test->xbind_addrs, xbe0, link);
+
+ TAILQ_FOREACH(xbe, &test->xbind_addrs, link) {
+ if (xbe->ai) {
+ freeaddrinfo(xbe->ai);
+ xbe->ai = NULL;
+ }
+ }
+
+ return retval;
+#else
+ i_errno = IENOSCTP;
+ return -1;
+#endif /* HAVE_SCTP_H */
+}
diff --git a/src/iperf_sctp.h b/src/iperf_sctp.h
new file mode 100644
index 0000000..764c410
--- /dev/null
+++ b/src/iperf_sctp.h
@@ -0,0 +1,68 @@
+/*
+ * iperf, Copyright (c) 2014, 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.
+ *
+ * If you have questions about your rights to use or distribute this
+ * software, please contact Berkeley Lab's Technology Transfer
+ * Department at TTD@lbl.gov.
+ *
+ * NOTICE. This software is owned by the U.S. Department of Energy.
+ * As such, the U.S. Government has been granted for itself and others
+ * acting on its behalf a paid-up, nonexclusive, irrevocable,
+ * worldwide license in the Software to reproduce, prepare derivative
+ * works, and perform publicly and display publicly. Beginning five
+ * (5) years after the date permission to assert copyright is obtained
+ * from the U.S. Department of Energy, and subject to any subsequent
+ * five (5) year renewals, the U.S. Government is granted for itself
+ * and others acting on its behalf a paid-up, nonexclusive,
+ * irrevocable, worldwide license in the Software to reproduce,
+ * prepare derivative works, distribute copies to the public, perform
+ * publicly and display publicly, and to permit others to do so.
+ *
+ * This code is distributed under a BSD style license, see the LICENSE
+ * file for complete information.
+ */
+#ifndef IPERF_SCTP_H
+#define IPERF_SCTP_H
+
+/**
+ * iperf_sctp_accept -- accepts a new SCTP connection
+ * on sctp_listener_socket for SCTP data and param/result
+ * exchange messages
+ *returns 0 on success
+ *
+ */
+int iperf_sctp_accept(struct iperf_test *);
+
+/**
+ * iperf_sctp_recv -- receives the data for sctp
+ * and the Param/result message exchange
+ *returns state of packet received
+ *
+ */
+int iperf_sctp_recv(struct iperf_stream *);
+
+
+/**
+ * iperf_sctp_send -- sends the client data for sctp
+ * and the Param/result message exchanges
+ * returns: bytes sent
+ *
+ */
+int iperf_sctp_send(struct iperf_stream *);
+
+
+int iperf_sctp_listen(struct iperf_test *);
+
+int iperf_sctp_connect(struct iperf_test *);
+
+int iperf_sctp_init(struct iperf_test *test);
+
+#define IPERF_SCTP_CLIENT 0
+#define IPERF_SCTP_SERVER 1
+
+int iperf_sctp_bindx(struct iperf_test *test, int s, int is_server);
+
+#endif
diff --git a/src/iperf_server_api.c b/src/iperf_server_api.c
new file mode 100644
index 0000000..77e9c35
--- /dev/null
+++ b/src/iperf_server_api.c
@@ -0,0 +1,918 @@
+/*
+ * 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.
+ *
+ * If you have questions about your rights to use or distribute this
+ * software, please contact Berkeley Lab's Technology Transfer
+ * Department at TTD@lbl.gov.
+ *
+ * NOTICE. This software is owned by the U.S. Department of Energy.
+ * As such, the U.S. Government has been granted for itself and others
+ * acting on its behalf a paid-up, nonexclusive, irrevocable,
+ * worldwide license in the Software to reproduce, prepare derivative
+ * works, and perform publicly and display publicly. Beginning five
+ * (5) years after the date permission to assert copyright is obtained
+ * from the U.S. Department of Energy, and subject to any subsequent
+ * five (5) year renewals, the U.S. Government is granted for itself
+ * and others acting on its behalf a paid-up, nonexclusive,
+ * irrevocable, worldwide license in the Software to reproduce,
+ * prepare derivative works, distribute copies to the public, perform
+ * publicly and display publicly, and to permit others to do so.
+ *
+ * This code is distributed under a BSD style license, see the LICENSE
+ * file for complete information.
+ */
+/* iperf_server_api.c: Functions to be used by an iperf server
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <errno.h>
+#include <unistd.h>
+#include <assert.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sched.h>
+#include <setjmp.h>
+
+#include "iperf.h"
+#include "iperf_api.h"
+#include "iperf_udp.h"
+#include "iperf_tcp.h"
+#include "iperf_util.h"
+#include "timer.h"
+#include "iperf_time.h"
+#include "net.h"
+#include "units.h"
+#include "iperf_util.h"
+#include "iperf_locale.h"
+
+#if defined(HAVE_TCP_CONGESTION)
+#if !defined(TCP_CA_NAME_MAX)
+#define TCP_CA_NAME_MAX 16
+#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;
+
+ /* Allow this thread to be cancelled even if it's in a syscall */
+ pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, 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)
+{
+ retry:
+ if((test->listener = netannounce(test->settings->domain, Ptcp, test->bind_address, test->bind_dev, test->server_port)) < 0) {
+ if (errno == EAFNOSUPPORT && (test->settings->domain == AF_INET6 || test->settings->domain == AF_UNSPEC)) {
+ /* If we get "Address family not supported by protocol", that
+ ** probably means we were compiled with IPv6 but the running
+ ** kernel does not actually do IPv6. This is not too unusual,
+ ** v6 support is and perhaps always will be spotty.
+ */
+ warning("this system does not seem to support IPv6 - trying IPv4");
+ test->settings->domain = AF_INET;
+ goto retry;
+ } else {
+ i_errno = IELISTEN;
+ return -1;
+ }
+ }
+
+ if (!test->json_output) {
+ if (test->server_last_run_rc != 2)
+ test->server_test_number +=1;
+ if (test->debug || test->server_last_run_rc != 2) {
+ iperf_printf(test, "-----------------------------------------------------------\n");
+ iperf_printf(test, "Server listening on %d (test #%d)\n", test->server_port, test->server_test_number);
+ iperf_printf(test, "-----------------------------------------------------------\n");
+ if (test->forceflush)
+ iflush(test);
+ }
+ }
+
+ FD_ZERO(&test->read_set);
+ FD_ZERO(&test->write_set);
+ FD_SET(test->listener, &test->read_set);
+ if (test->listener > test->max_fd) test->max_fd = test->listener;
+
+ return 0;
+}
+
+int
+iperf_accept(struct iperf_test *test)
+{
+ int s;
+ signed char rbuf = ACCESS_DENIED;
+ socklen_t len;
+ struct sockaddr_storage addr;
+
+ len = sizeof(addr);
+ if ((s = accept(test->listener, (struct sockaddr *) &addr, &len)) < 0) {
+ i_errno = IEACCEPT;
+ return -1;
+ }
+
+ if (test->ctrl_sck == -1) {
+ /* Server free, accept new client */
+ test->ctrl_sck = s;
+ // set TCP_NODELAY for lower latency on control messages
+ int flag = 1;
+ if (setsockopt(test->ctrl_sck, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int))) {
+ i_errno = IESETNODELAY;
+ return -1;
+ }
+
+#if defined(HAVE_TCP_USER_TIMEOUT)
+ int opt;
+ if ((opt = test->settings->snd_timeout)) {
+ if (setsockopt(s, IPPROTO_TCP, TCP_USER_TIMEOUT, &opt, sizeof(opt)) < 0) {
+ i_errno = IESETUSERTIMEOUT;
+ return -1;
+ }
+ }
+#endif /* HAVE_TCP_USER_TIMEOUT */
+
+ 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;
+ return -1;
+ }
+ 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_exchange_parameters(test) < 0)
+ return -1;
+ if (test->server_affinity != -1)
+ if (iperf_setaffinity(test, test->server_affinity) != 0)
+ return -1;
+ 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.
+ * 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");
+ } else {
+ if (test->debug)
+ printf("successfully sent ACCESS_DENIED to an unsolicited connection request during active test\n");
+ }
+ close(s);
+ }
+
+ return 0;
+}
+
+
+/**************************************************************************/
+int
+iperf_handle_message_server(struct iperf_test *test)
+{
+ int rval;
+ struct iperf_stream *sp;
+
+ // 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");
+ i_errno = IECTRLCLOSE;
+ test->state = IPERF_DONE;
+ return 0;
+ } else {
+ i_errno = IERECVMESSAGE;
+ return -1;
+ }
+ }
+
+ switch(test->state) {
+ case TEST_START:
+ break;
+ case TEST_END:
+ test->done = 1;
+ cpu_util(test->cpu_util);
+ test->stats_callback(test);
+ SLIST_FOREACH(sp, &test->streams, streams) {
+ FD_CLR(sp->socket, &test->read_set);
+ FD_CLR(sp->socket, &test->write_set);
+ close(sp->socket);
+ }
+ test->reporter_callback(test);
+ 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)
+ return -1;
+ if (test->on_test_finish)
+ test->on_test_finish(test);
+ break;
+ case IPERF_DONE:
+ break;
+ case CLIENT_TERMINATE:
+ i_errno = IECLIENTTERM;
+
+ // Temporarily be in DISPLAY_RESULTS phase so we can get
+ // ending summary statistics.
+ signed char oldstate = test->state;
+ cpu_util(test->cpu_util);
+ test->state = DISPLAY_RESULTS;
+ test->reporter_callback(test);
+ test->state = oldstate;
+
+ // XXX: Remove this line below!
+ iperf_err(test, "the client has terminated");
+ SLIST_FOREACH(sp, &test->streams, streams) {
+ FD_CLR(sp->socket, &test->read_set);
+ FD_CLR(sp->socket, &test->write_set);
+ close(sp->socket);
+ }
+ test->state = IPERF_DONE;
+ break;
+ default:
+ i_errno = IEMESSAGE;
+ return -1;
+ }
+
+ return 0;
+}
+
+static void
+server_timer_proc(TimerClientData client_data, struct iperf_time *nowP)
+{
+ struct iperf_test *test = client_data.p;
+ struct iperf_stream *sp;
+
+ test->timer = NULL;
+ if (test->done)
+ return;
+ test->done = 1;
+ /* 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);
+ }
+ close(test->ctrl_sck);
+ test->ctrl_sck = -1;
+}
+
+static void
+server_stats_timer_proc(TimerClientData client_data, struct iperf_time *nowP)
+{
+ struct iperf_test *test = client_data.p;
+
+ if (test->done)
+ return;
+ if (test->stats_callback)
+ test->stats_callback(test);
+}
+
+static void
+server_reporter_timer_proc(TimerClientData client_data, struct iperf_time *nowP)
+{
+ struct iperf_test *test = client_data.p;
+
+ if (test->done)
+ return;
+ if (test->reporter_callback)
+ test->reporter_callback(test);
+}
+
+static int
+create_server_timers(struct iperf_test * test)
+{
+ struct iperf_time now;
+ TimerClientData cd;
+ int max_rtt = 4; /* seconds */
+ int state_transitions = 10; /* number of state transitions in iperf3 */
+ int grace_period = max_rtt * state_transitions;
+
+ if (iperf_time_now(&now) < 0) {
+ i_errno = IEINITTEST;
+ return -1;
+ }
+ cd.p = test;
+ test->timer = test->stats_timer = test->reporter_timer = NULL;
+ if (test->duration != 0 ) {
+ test->done = 0;
+ test->timer = tmr_create(&now, server_timer_proc, cd, (test->duration + test->omit + grace_period) * SEC_TO_US, 0);
+ if (test->timer == NULL) {
+ i_errno = IEINITTEST;
+ return -1;
+ }
+ }
+
+ test->stats_timer = test->reporter_timer = NULL;
+ if (test->stats_interval != 0) {
+ test->stats_timer = tmr_create(&now, server_stats_timer_proc, cd, test->stats_interval * SEC_TO_US, 1);
+ if (test->stats_timer == NULL) {
+ i_errno = IEINITTEST;
+ return -1;
+ }
+ }
+ if (test->reporter_interval != 0) {
+ test->reporter_timer = tmr_create(&now, server_reporter_timer_proc, cd, test->reporter_interval * SEC_TO_US, 1);
+ if (test->reporter_timer == NULL) {
+ i_errno = IEINITTEST;
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static void
+server_omit_timer_proc(TimerClientData client_data, struct iperf_time *nowP)
+{
+ struct iperf_test *test = client_data.p;
+
+ test->omit_timer = NULL;
+ test->omitting = 0;
+ iperf_reset_stats(test);
+ if (test->verbose && !test->json_output && test->reporter_interval == 0)
+ iperf_printf(test, "%s", report_omit_done);
+
+ /* Reset the timers. */
+ if (test->stats_timer != NULL)
+ tmr_reset(nowP, test->stats_timer);
+ if (test->reporter_timer != NULL)
+ tmr_reset(nowP, test->reporter_timer);
+}
+
+static int
+create_server_omit_timer(struct iperf_test * test)
+{
+ struct iperf_time now;
+ TimerClientData cd;
+
+ if (test->omit == 0) {
+ test->omit_timer = NULL;
+ test->omitting = 0;
+ } else {
+ if (iperf_time_now(&now) < 0) {
+ i_errno = IEINITTEST;
+ return -1;
+ }
+ test->omitting = 1;
+ cd.p = test;
+ test->omit_timer = tmr_create(&now, server_omit_timer_proc, cd, test->omit * SEC_TO_US, 0);
+ if (test->omit_timer == NULL) {
+ i_errno = IEINITTEST;
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static void
+cleanup_server(struct iperf_test *test)
+{
+ struct iperf_stream *sp;
+
+ /* Cancel outstanding threads */
+ int i_errno_save = i_errno;
+ SLIST_FOREACH(sp, &test->streams, streams) {
+ int rc;
+ sp->done = 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);
+ }
+ }
+ 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) {
+ if (sp->socket > -1) {
+ FD_CLR(sp->socket, &test->read_set);
+ FD_CLR(sp->socket, &test->write_set);
+ close(sp->socket);
+ sp->socket = -1;
+ }
+ }
+
+ /* Close open test sockets */
+ if (test->ctrl_sck > -1) {
+ close(test->ctrl_sck);
+ test->ctrl_sck = -1;
+ }
+ if (test->listener > -1) {
+ close(test->listener);
+ test->listener = -1;
+ }
+ if (test->prot_listener > -1) { // May remain open if create socket failed
+ close(test->prot_listener);
+ test->prot_listener = -1;
+ }
+
+ /* Cancel any remaining timers. */
+ 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;
+ }
+ if (test->omit_timer != NULL) {
+ tmr_cancel(test->omit_timer);
+ test->omit_timer = NULL;
+ }
+ if (test->congestion_used != NULL) {
+ free(test->congestion_used);
+ test->congestion_used = NULL;
+ }
+ if (test->timer != NULL) {
+ tmr_cancel(test->timer);
+ test->timer = NULL;
+ }
+}
+
+
+int
+iperf_run_server(struct iperf_test *test)
+{
+ int result, s;
+ int send_streams_accepted, rec_streams_accepted;
+ int streams_to_send = 0, streams_to_rec = 0;
+#if defined(HAVE_TCP_CONGESTION)
+ int saved_errno;
+#endif /* HAVE_TCP_CONGESTION */
+ fd_set read_set, write_set;
+ struct iperf_stream *sp;
+ struct iperf_time now;
+ struct iperf_time last_receive_time;
+ 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;
+
+ if (test->logfile)
+ if (iperf_open_logfile(test) < 0)
+ return -2;
+
+ if (test->affinity != -1)
+ if (iperf_setaffinity(test, test->affinity) != 0) {
+ cleanup_server(test);
+ return -2;
+ }
+
+ 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));
+ cJSON_AddItemToObject(test->json_start, "system_info", cJSON_CreateString(get_system_info()));
+ } else if (test->verbose) {
+ iperf_printf(test, "%s\n", version);
+ iperf_printf(test, "%s", "");
+ iperf_printf(test, "%s\n", get_system_info());
+ iflush(test);
+ }
+
+ // Open socket and listen
+ if (iperf_server_listen(test) < 0) {
+ cleanup_server(test);
+ return -2;
+ }
+
+ iperf_time_now(&last_receive_time); // Initialize last time something was received
+ last_receive_blocks = 0;
+
+ test->state = 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)
+ if (test->bitrate_limit_exceeded) {
+ cleanup_server(test);
+ i_errno = IETOTALRATE;
+ return -1;
+ }
+
+ memcpy(&read_set, &test->read_set, sizeof(fd_set));
+ memcpy(&write_set, &test->write_set, sizeof(fd_set));
+
+ iperf_time_now(&now);
+ timeout = tmr_timeout(&now);
+
+ // Ensure select() will timeout to allow handling error cases that require server restart
+ if (test->state == IPERF_START) { // In idle mode server may need to restart
+ if (timeout == NULL && test->settings->idle_timeout > 0) {
+ used_timeout.tv_sec = test->settings->idle_timeout;
+ used_timeout.tv_usec = 0;
+ timeout = &used_timeout;
+ }
+ } else if (test->mode != SENDER) { // In non-reverse active mode server ensures data is received
+ timeout_us = -1;
+ if (timeout != NULL) {
+ used_timeout.tv_sec = timeout->tv_sec;
+ 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;
+ }
+ timeout = &used_timeout;
+ }
+
+ result = select(test->max_fd + 1, &read_set, &write_set, NULL, timeout);
+ if (result < 0 && errno != EINTR) {
+ cleanup_server(test);
+ i_errno = IESELECT;
+ return -1;
+ } else if (result == 0) {
+ /*
+ * 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;
+ if (test->debug)
+ printf("Server restart (#%d) in idle state as no connection request was received for %d sec\n",
+ test->server_forced_idle_restarts_count, test->settings->idle_timeout);
+ cleanup_server(test);
+ if ( iperf_get_test_one_off(test) ) {
+ if (test->debug)
+ printf("No connection request was received for %d sec in one-off mode; exiting.\n",
+ test->settings->idle_timeout);
+ exit(0);
+ }
+
+ 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) {
+ /* 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) {
+ if (FD_ISSET(test->listener, &read_set)) {
+ if (test->state != CREATE_STREAMS) {
+ if (iperf_accept(test) < 0) {
+ cleanup_server(test);
+ return -1;
+ }
+ FD_CLR(test->listener, &read_set);
+
+ // Set streams number
+ if (test->mode == BIDIRECTIONAL) {
+ streams_to_send = test->num_streams;
+ streams_to_rec = test->num_streams;
+ } else if (test->mode == RECEIVER) {
+ streams_to_rec = test->num_streams;
+ streams_to_send = 0;
+ } else {
+ streams_to_send = test->num_streams;
+ streams_to_rec = 0;
+ }
+ }
+ }
+ if (FD_ISSET(test->ctrl_sck, &read_set)) {
+ if (iperf_handle_message_server(test) < 0) {
+ cleanup_server(test);
+ return -1;
+ }
+ FD_CLR(test->ctrl_sck, &read_set);
+ }
+
+ if (test->state == CREATE_STREAMS) {
+ if (FD_ISSET(test->prot_listener, &read_set)) {
+
+ if ((s = test->protocol->accept(test)) < 0) {
+ cleanup_server(test);
+ return -1;
+ }
+
+ /* apply other common socket options */
+ if (iperf_common_sockopts(test, s) < 0)
+ {
+ cleanup_server(test);
+ return -1;
+ }
+
+ if (!is_closed(s)) {
+
+#if defined(HAVE_TCP_USER_TIMEOUT)
+ if (test->protocol->id == Ptcp) {
+ int opt;
+ if ((opt = test->settings->snd_timeout)) {
+ if (setsockopt(s, IPPROTO_TCP, TCP_USER_TIMEOUT, &opt, sizeof(opt)) < 0) {
+ saved_errno = errno;
+ close(s);
+ cleanup_server(test);
+ errno = saved_errno;
+ i_errno = IESETUSERTIMEOUT;
+ return -1;
+ }
+ }
+ }
+#endif /* HAVE_TCP_USER_TIMEOUT */
+
+#if defined(HAVE_TCP_CONGESTION)
+ if (test->protocol->id == Ptcp) {
+ if (test->congestion) {
+ if (setsockopt(s, IPPROTO_TCP, TCP_CONGESTION, test->congestion, strlen(test->congestion)) < 0) {
+ /*
+ * ENOENT means we tried to set the
+ * congestion algorithm but the algorithm
+ * specified doesn't exist. This can happen
+ * if the client and server have different
+ * congestion algorithms available. In this
+ * case, print a warning, but otherwise
+ * continue.
+ */
+ if (errno == ENOENT) {
+ warning("TCP congestion control algorithm not supported");
+ }
+ else {
+ saved_errno = errno;
+ close(s);
+ cleanup_server(test);
+ errno = saved_errno;
+ i_errno = IESETCONGESTION;
+ return -1;
+ }
+ }
+ }
+ {
+ socklen_t len = TCP_CA_NAME_MAX;
+ char ca[TCP_CA_NAME_MAX + 1];
+ int rc;
+ rc = getsockopt(s, IPPROTO_TCP, TCP_CONGESTION, ca, &len);
+ if (rc < 0 && test->congestion) {
+ saved_errno = errno;
+ close(s);
+ cleanup_server(test);
+ errno = saved_errno;
+ i_errno = IESETCONGESTION;
+ return -1;
+ }
+ /*
+ * If not the first connection, discard prior
+ * congestion algorithm name so we don't leak
+ * duplicated strings. We probably don't need
+ * the old string anyway.
+ */
+ if (test->congestion_used != NULL) {
+ 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");
+ else
+ test->congestion_used = strdup(ca);
+ if (test->debug) {
+ printf("Congestion algorithm is %s\n", test->congestion_used);
+ }
+ }
+ }
+#endif /* HAVE_TCP_CONGESTION */
+
+ if (rec_streams_accepted != streams_to_rec) {
+ flag = 0;
+ ++rec_streams_accepted;
+ } else if (send_streams_accepted != streams_to_send) {
+ flag = 1;
+ ++send_streams_accepted;
+ }
+
+ if (flag != -1) {
+ sp = iperf_new_stream(test, s, flag);
+ if (!sp) {
+ cleanup_server(test);
+ return -1;
+ }
+
+ if (s > test->max_fd) test->max_fd = s;
+
+ if (test->on_new_stream)
+ test->on_new_stream(sp);
+
+ flag = -1;
+ }
+ }
+ FD_CLR(test->prot_listener, &read_set);
+ }
+
+
+ if (rec_streams_accepted == streams_to_rec && send_streams_accepted == streams_to_send) {
+ if (test->protocol->id != Ptcp) {
+ FD_CLR(test->prot_listener, &test->read_set);
+ close(test->prot_listener);
+ test->prot_listener = -1;
+ } else {
+ if (test->no_delay || test->settings->mss || test->settings->socket_bufsize) {
+ FD_CLR(test->listener, &test->read_set);
+ close(test->listener);
+ test->listener = -1;
+ if ((s = netannounce(test->settings->domain, Ptcp, test->bind_address, test->bind_dev, test->server_port)) < 0) {
+ cleanup_server(test);
+ i_errno = IELISTEN;
+ return -1;
+ }
+ test->listener = s;
+ FD_SET(test->listener, &test->read_set);
+ if (test->listener > test->max_fd) test->max_fd = test->listener;
+ }
+ }
+ test->prot_listener = -1;
+
+ /* Ensure that total requested data rate is not above limit */
+ iperf_size_t total_requested_rate = test->num_streams * test->settings->rate * (test->mode == BIDIRECTIONAL? 2 : 1);
+ if (test->settings->bitrate_limit > 0 && total_requested_rate > test->settings->bitrate_limit) {
+ if (iperf_get_verbose(test))
+ iperf_err(test, "Client total requested throughput rate of %" PRIu64 " bps exceeded %" PRIu64 " bps limit",
+ total_requested_rate, test->settings->bitrate_limit);
+ cleanup_server(test);
+ i_errno = IETOTALRATE;
+ return -1;
+ }
+
+ // Begin calculating CPU utilization
+ cpu_util(NULL);
+
+ if (iperf_set_send_state(test, TEST_START) != 0) {
+ cleanup_server(test);
+ return -1;
+ }
+ if (iperf_init_test(test) < 0) {
+ cleanup_server(test);
+ return -1;
+ }
+ if (create_server_timers(test) < 0) {
+ cleanup_server(test);
+ return -1;
+ }
+ if (create_server_omit_timer(test) < 0) {
+ cleanup_server(test);
+ return -1;
+ }
+ if (test->mode != RECEIVER)
+ if (iperf_create_send_timers(test) < 0) {
+ cleanup_server(test);
+ return -1;
+ }
+ if (iperf_set_send_state(test, TEST_RUNNING) != 0) {
+ cleanup_server(test);
+ return -1;
+ }
+
+ /* Create and spin up threads */
+ pthread_attr_t attr;
+ if (pthread_attr_init(&attr) != 0) {
+ i_errno = IEPTHREADATTRINIT;
+ cleanup_server(test);
+ };
+
+ 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;
+ }
+ 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;
+ cleanup_server(test);
+ };
+ }
+ }
+ }
+
+ if (result == 0 ||
+ (timeout != NULL && timeout->tv_sec == 0 && timeout->tv_usec == 0)) {
+ /* Run the timers. */
+ iperf_time_now(&now);
+ tmr_run(&now);
+ }
+ }
+
+
+ if (test->json_output) {
+ if (iperf_json_finish(test) < 0)
+ return -1;
+ }
+
+ iflush(test);
+ cleanup_server(test);
+
+ if (test->server_affinity != -1)
+ if (iperf_clearaffinity(test) != 0)
+ return -1;
+
+ return 0;
+}
diff --git a/src/iperf_tcp.c b/src/iperf_tcp.c
new file mode 100644
index 0000000..ce6a522
--- /dev/null
+++ b/src/iperf_tcp.c
@@ -0,0 +1,583 @@
+/*
+ * iperf, Copyright (c) 2014-2022, 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.
+ *
+ * If you have questions about your rights to use or distribute this
+ * software, please contact Berkeley Lab's Technology Transfer
+ * Department at TTD@lbl.gov.
+ *
+ * NOTICE. This software is owned by the U.S. Department of Energy.
+ * As such, the U.S. Government has been granted for itself and others
+ * acting on its behalf a paid-up, nonexclusive, irrevocable,
+ * worldwide license in the Software to reproduce, prepare derivative
+ * works, and perform publicly and display publicly. Beginning five
+ * (5) years after the date permission to assert copyright is obtained
+ * from the U.S. Department of Energy, and subject to any subsequent
+ * five (5) year renewals, the U.S. Government is granted for itself
+ * and others acting on its behalf a paid-up, nonexclusive,
+ * irrevocable, worldwide license in the Software to reproduce,
+ * prepare derivative works, distribute copies to the public, perform
+ * publicly and display publicly, and to permit others to do so.
+ *
+ * This code is distributed under a BSD style license, see the LICENSE
+ * file for complete information.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <sys/time.h>
+#include <sys/select.h>
+#include <limits.h>
+
+#include "iperf.h"
+#include "iperf_api.h"
+#include "iperf_tcp.h"
+#include "net.h"
+#include "cjson.h"
+
+#if defined(HAVE_FLOWLABEL)
+#include "flowlabel.h"
+#endif /* HAVE_FLOWLABEL */
+
+/* iperf_tcp_recv
+ *
+ * receives the data for TCP
+ */
+int
+iperf_tcp_recv(struct iperf_stream *sp)
+{
+ int r;
+
+ 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;
+ }
+ else {
+ if (sp->test->debug)
+ printf("Late receive, state = %d\n", sp->test->state);
+ }
+
+ return r;
+}
+
+
+/* iperf_tcp_send
+ *
+ * sends the data for TCP
+ */
+int
+iperf_tcp_send(struct iperf_stream *sp)
+{
+ int r;
+
+ if (!sp->pending_size)
+ sp->pending_size = sp->settings->blksize;
+
+ if (sp->test->zerocopy)
+ r = Nsendfile(sp->buffer_fd, sp->socket, sp->buffer, sp->pending_size);
+ else
+ r = Nwrite(sp->socket, sp->buffer, sp->pending_size, Ptcp);
+
+ if (r < 0)
+ return r;
+
+ sp->pending_size -= r;
+ sp->result->bytes_sent += r;
+ 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);
+
+ return r;
+}
+
+
+/* iperf_tcp_accept
+ *
+ * accept a new TCP stream connection
+ */
+int
+iperf_tcp_accept(struct iperf_test * test)
+{
+ int s;
+ signed char rbuf = ACCESS_DENIED;
+ char cookie[COOKIE_SIZE];
+ socklen_t len;
+ struct sockaddr_storage addr;
+
+ len = sizeof(addr);
+ if ((s = accept(test->listener, (struct sockaddr *) &addr, &len)) < 0) {
+ i_errno = IESTREAMCONNECT;
+ return -1;
+ }
+
+ if (Nread(s, cookie, COOKIE_SIZE, Ptcp) < 0) {
+ i_errno = IERECVCOOKIE;
+ return -1;
+ }
+
+ if (strcmp(test->cookie, cookie) != 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);
+ }
+ close(s);
+ }
+
+ return s;
+}
+
+
+/* iperf_tcp_listen
+ *
+ * start up a listener for TCP stream connections
+ */
+int
+iperf_tcp_listen(struct iperf_test *test)
+{
+ int s, opt;
+ socklen_t optlen;
+ int saved_errno;
+ int rcvbuf_actual, sndbuf_actual;
+
+ s = test->listener;
+
+ /*
+ * If certain parameters are specified (such as socket buffer
+ * size), then throw away the listening socket (the one for which
+ * we just accepted the control connection) and recreate it with
+ * those parameters. That way, when new data connections are
+ * set, they'll have all the correct parameters in place.
+ *
+ * It's not clear whether this is a requirement or a convenience.
+ */
+ if (test->no_delay || test->settings->mss || test->settings->socket_bufsize) {
+ struct addrinfo hints, *res;
+ char portstr[6];
+
+ FD_CLR(s, &test->read_set);
+ close(s);
+
+ snprintf(portstr, 6, "%d", test->server_port);
+ memset(&hints, 0, sizeof(hints));
+
+ /*
+ * 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().
+ */
+ if (test->settings->domain == AF_UNSPEC && !test->bind_address) {
+ hints.ai_family = AF_INET6;
+ }
+ else {
+ hints.ai_family = test->settings->domain;
+ }
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = AI_PASSIVE;
+ if ((gerror = getaddrinfo(test->bind_address, portstr, &hints, &res)) != 0) {
+ i_errno = IESTREAMLISTEN;
+ return -1;
+ }
+
+ if ((s = socket(res->ai_family, SOCK_STREAM, 0)) < 0) {
+ freeaddrinfo(res);
+ i_errno = IESTREAMLISTEN;
+ return -1;
+ }
+
+ if (test->no_delay) {
+ opt = 1;
+ if (setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt)) < 0) {
+ saved_errno = errno;
+ close(s);
+ freeaddrinfo(res);
+ errno = saved_errno;
+ i_errno = IESETNODELAY;
+ return -1;
+ }
+ }
+ // XXX: Setting MSS is very buggy!
+ if ((opt = test->settings->mss)) {
+ if (setsockopt(s, IPPROTO_TCP, TCP_MAXSEG, &opt, sizeof(opt)) < 0) {
+ saved_errno = errno;
+ close(s);
+ freeaddrinfo(res);
+ errno = saved_errno;
+ i_errno = IESETMSS;
+ return -1;
+ }
+ }
+ if ((opt = test->settings->socket_bufsize)) {
+ if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt)) < 0) {
+ saved_errno = errno;
+ close(s);
+ freeaddrinfo(res);
+ errno = saved_errno;
+ i_errno = IESETBUF;
+ return -1;
+ }
+ if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, &opt, sizeof(opt)) < 0) {
+ saved_errno = errno;
+ close(s);
+ freeaddrinfo(res);
+ errno = saved_errno;
+ i_errno = IESETBUF;
+ 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) {
+ if (test->debug) {
+ printf("Setting application pacing to %u\n", rate);
+ }
+ }
+ }
+ opt = 1;
+ if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) {
+ saved_errno = errno;
+ close(s);
+ freeaddrinfo(res);
+ errno = saved_errno;
+ i_errno = IEREUSEADDR;
+ return -1;
+ }
+
+ /*
+ * If we got an IPv6 socket, figure out if it should accept IPv4
+ * connections as well. See documentation in netannounce() for
+ * more details.
+ */
+#if defined(IPV6_V6ONLY) && !defined(__OpenBSD__)
+ if (res->ai_family == AF_INET6 && (test->settings->domain == AF_UNSPEC || test->settings->domain == AF_INET)) {
+ if (test->settings->domain == AF_UNSPEC)
+ opt = 0;
+ else
+ opt = 1;
+ if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY,
+ (char *) &opt, sizeof(opt)) < 0) {
+ saved_errno = errno;
+ close(s);
+ freeaddrinfo(res);
+ errno = saved_errno;
+ i_errno = IEV6ONLY;
+ return -1;
+ }
+ }
+#endif /* IPV6_V6ONLY */
+
+ if (bind(s, (struct sockaddr *) res->ai_addr, res->ai_addrlen) < 0) {
+ saved_errno = errno;
+ close(s);
+ freeaddrinfo(res);
+ errno = saved_errno;
+ i_errno = IESTREAMLISTEN;
+ return -1;
+ }
+
+ freeaddrinfo(res);
+
+ if (listen(s, INT_MAX) < 0) {
+ i_errno = IESTREAMLISTEN;
+ return -1;
+ }
+
+ test->listener = s;
+ }
+
+ /* Read back and verify the sender socket buffer size */
+ optlen = sizeof(sndbuf_actual);
+ if (getsockopt(s, SOL_SOCKET, SO_SNDBUF, &sndbuf_actual, &optlen) < 0) {
+ saved_errno = errno;
+ close(s);
+ errno = saved_errno;
+ i_errno = IESETBUF;
+ return -1;
+ }
+ if (test->debug) {
+ printf("SNDBUF is %u, expecting %u\n", sndbuf_actual, test->settings->socket_bufsize);
+ }
+ if (test->settings->socket_bufsize && test->settings->socket_bufsize > sndbuf_actual) {
+ i_errno = IESETBUF2;
+ return -1;
+ }
+
+ /* Read back and verify the receiver socket buffer size */
+ optlen = sizeof(rcvbuf_actual);
+ if (getsockopt(s, SOL_SOCKET, SO_RCVBUF, &rcvbuf_actual, &optlen) < 0) {
+ saved_errno = errno;
+ close(s);
+ errno = saved_errno;
+ i_errno = IESETBUF;
+ return -1;
+ }
+ if (test->debug) {
+ printf("RCVBUF is %u, expecting %u\n", rcvbuf_actual, test->settings->socket_bufsize);
+ }
+ if (test->settings->socket_bufsize && test->settings->socket_bufsize > rcvbuf_actual) {
+ i_errno = IESETBUF2;
+ return -1;
+ }
+
+ if (test->json_output) {
+ cJSON_AddNumberToObject(test->json_start, "sock_bufsize", test->settings->socket_bufsize);
+ cJSON_AddNumberToObject(test->json_start, "sndbuf_actual", sndbuf_actual);
+ cJSON_AddNumberToObject(test->json_start, "rcvbuf_actual", rcvbuf_actual);
+ }
+
+ return s;
+}
+
+
+/* iperf_tcp_connect
+ *
+ * connect to a TCP stream listener
+ * This function is roughly similar to netdial(), and may indeed have
+ * been derived from it at some point, but it sets many TCP-specific
+ * options between socket creation and connection.
+ */
+int
+iperf_tcp_connect(struct iperf_test *test)
+{
+ struct addrinfo *server_res;
+ int s, opt;
+ socklen_t optlen;
+ int saved_errno;
+ int rcvbuf_actual, sndbuf_actual;
+
+ 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);
+ if (s < 0) {
+ i_errno = IESTREAMCONNECT;
+ return -1;
+ }
+
+ /* Set socket options */
+ if (test->no_delay) {
+ opt = 1;
+ if (setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt)) < 0) {
+ saved_errno = errno;
+ close(s);
+ freeaddrinfo(server_res);
+ errno = saved_errno;
+ i_errno = IESETNODELAY;
+ return -1;
+ }
+ }
+ if ((opt = test->settings->mss)) {
+ if (setsockopt(s, IPPROTO_TCP, TCP_MAXSEG, &opt, sizeof(opt)) < 0) {
+ saved_errno = errno;
+ close(s);
+ freeaddrinfo(server_res);
+ errno = saved_errno;
+ i_errno = IESETMSS;
+ return -1;
+ }
+ }
+ if ((opt = test->settings->socket_bufsize)) {
+ if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt)) < 0) {
+ saved_errno = errno;
+ close(s);
+ freeaddrinfo(server_res);
+ errno = saved_errno;
+ i_errno = IESETBUF;
+ return -1;
+ }
+ if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, &opt, sizeof(opt)) < 0) {
+ saved_errno = errno;
+ close(s);
+ freeaddrinfo(server_res);
+ errno = saved_errno;
+ i_errno = IESETBUF;
+ return -1;
+ }
+ }
+#if defined(HAVE_TCP_USER_TIMEOUT)
+ if ((opt = test->settings->snd_timeout)) {
+ if (setsockopt(s, IPPROTO_TCP, TCP_USER_TIMEOUT, &opt, sizeof(opt)) < 0) {
+ saved_errno = errno;
+ close(s);
+ freeaddrinfo(server_res);
+ errno = saved_errno;
+ i_errno = IESETUSERTIMEOUT;
+ return -1;
+ }
+ }
+#endif /* HAVE_TCP_USER_TIMEOUT */
+
+ /* Read back and verify the sender socket buffer size */
+ optlen = sizeof(sndbuf_actual);
+ if (getsockopt(s, SOL_SOCKET, SO_SNDBUF, &sndbuf_actual, &optlen) < 0) {
+ saved_errno = errno;
+ close(s);
+ freeaddrinfo(server_res);
+ errno = saved_errno;
+ i_errno = IESETBUF;
+ return -1;
+ }
+ if (test->debug) {
+ printf("SNDBUF is %u, expecting %u\n", sndbuf_actual, test->settings->socket_bufsize);
+ }
+ if (test->settings->socket_bufsize && test->settings->socket_bufsize > sndbuf_actual) {
+ i_errno = IESETBUF2;
+ return -1;
+ }
+
+ /* Read back and verify the receiver socket buffer size */
+ optlen = sizeof(rcvbuf_actual);
+ if (getsockopt(s, SOL_SOCKET, SO_RCVBUF, &rcvbuf_actual, &optlen) < 0) {
+ saved_errno = errno;
+ close(s);
+ freeaddrinfo(server_res);
+ errno = saved_errno;
+ i_errno = IESETBUF;
+ return -1;
+ }
+ if (test->debug) {
+ printf("RCVBUF is %u, expecting %u\n", rcvbuf_actual, test->settings->socket_bufsize);
+ }
+ if (test->settings->socket_bufsize && test->settings->socket_bufsize > rcvbuf_actual) {
+ i_errno = IESETBUF2;
+ return -1;
+ }
+
+ if (test->json_output) {
+ cJSON *sock_bufsize_item = cJSON_GetObjectItem(test->json_start, "sock_bufsize");
+ if (sock_bufsize_item == NULL) {
+ cJSON_AddNumberToObject(test->json_start, "sock_bufsize", test->settings->socket_bufsize);
+ }
+
+ cJSON *sndbuf_actual_item = cJSON_GetObjectItem(test->json_start, "sndbuf_actual");
+ if (sndbuf_actual_item == NULL) {
+ cJSON_AddNumberToObject(test->json_start, "sndbuf_actual", sndbuf_actual);
+ }
+
+ cJSON *rcvbuf_actual_item = cJSON_GetObjectItem(test->json_start, "rcvbuf_actual");
+ if (rcvbuf_actual_item == NULL) {
+ cJSON_AddNumberToObject(test->json_start, "rcvbuf_actual", rcvbuf_actual);
+ }
+ }
+
+#if defined(HAVE_FLOWLABEL)
+ if (test->settings->flowlabel) {
+ if (server_res->ai_addr->sa_family != AF_INET6) {
+ saved_errno = errno;
+ close(s);
+ freeaddrinfo(server_res);
+ errno = saved_errno;
+ i_errno = IESETFLOW;
+ return -1;
+ } else {
+ struct sockaddr_in6* sa6P = (struct sockaddr_in6*) server_res->ai_addr;
+ char freq_buf[sizeof(struct in6_flowlabel_req)];
+ struct in6_flowlabel_req *freq = (struct in6_flowlabel_req *)freq_buf;
+ int freq_len = sizeof(*freq);
+
+ memset(freq, 0, sizeof(*freq));
+ freq->flr_label = htonl(test->settings->flowlabel & IPV6_FLOWINFO_FLOWLABEL);
+ freq->flr_action = IPV6_FL_A_GET;
+ freq->flr_flags = IPV6_FL_F_CREATE;
+ freq->flr_share = IPV6_FL_S_ANY;
+ memcpy(&freq->flr_dst, &sa6P->sin6_addr, 16);
+
+ if (setsockopt(s, IPPROTO_IPV6, IPV6_FLOWLABEL_MGR, freq, freq_len) < 0) {
+ saved_errno = errno;
+ close(s);
+ freeaddrinfo(server_res);
+ errno = saved_errno;
+ i_errno = IESETFLOW;
+ return -1;
+ }
+ sa6P->sin6_flowinfo = freq->flr_label;
+
+ opt = 1;
+ if (setsockopt(s, IPPROTO_IPV6, IPV6_FLOWINFO_SEND, &opt, sizeof(opt)) < 0) {
+ saved_errno = errno;
+ close(s);
+ freeaddrinfo(server_res);
+ errno = saved_errno;
+ i_errno = IESETFLOW;
+ return -1;
+ }
+ }
+ }
+#endif /* HAVE_FLOWLABEL */
+
+#if defined(HAVE_SO_MAX_PACING_RATE)
+ /* 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;
+ 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) {
+ if (test->debug) {
+ printf("Setting application pacing to %u\n", rate);
+ }
+ }
+ }
+
+ /* Set common socket options */
+ iperf_common_sockopts(test, s);
+
+ if (connect(s, (struct sockaddr *) server_res->ai_addr, server_res->ai_addrlen) < 0 && errno != EINPROGRESS) {
+ saved_errno = errno;
+ close(s);
+ freeaddrinfo(server_res);
+ errno = saved_errno;
+ i_errno = IESTREAMCONNECT;
+ return -1;
+ }
+
+ freeaddrinfo(server_res);
+
+ /* Send cookie for verification */
+ if (Nwrite(s, test->cookie, COOKIE_SIZE, Ptcp) < 0) {
+ saved_errno = errno;
+ close(s);
+ errno = saved_errno;
+ i_errno = IESENDCOOKIE;
+ return -1;
+ }
+
+ return s;
+}
diff --git a/src/iperf_tcp.h b/src/iperf_tcp.h
new file mode 100644
index 0000000..d53b052
--- /dev/null
+++ b/src/iperf_tcp.h
@@ -0,0 +1,63 @@
+/*
+ * iperf, Copyright (c) 2014, 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.
+ *
+ * If you have questions about your rights to use or distribute this
+ * software, please contact Berkeley Lab's Technology Transfer
+ * Department at TTD@lbl.gov.
+ *
+ * NOTICE. This software is owned by the U.S. Department of Energy.
+ * As such, the U.S. Government has been granted for itself and others
+ * acting on its behalf a paid-up, nonexclusive, irrevocable,
+ * worldwide license in the Software to reproduce, prepare derivative
+ * works, and perform publicly and display publicly. Beginning five
+ * (5) years after the date permission to assert copyright is obtained
+ * from the U.S. Department of Energy, and subject to any subsequent
+ * five (5) year renewals, the U.S. Government is granted for itself
+ * and others acting on its behalf a paid-up, nonexclusive,
+ * irrevocable, worldwide license in the Software to reproduce,
+ * prepare derivative works, distribute copies to the public, perform
+ * publicly and display publicly, and to permit others to do so.
+ *
+ * This code is distributed under a BSD style license, see the LICENSE
+ * file for complete information.
+ */
+#ifndef IPERF_TCP_H
+#define IPERF_TCP_H
+
+
+/**
+ * iperf_tcp_accept -- accepts a new TCP connection
+ * on tcp_listener_socket for TCP data and param/result
+ * exchange messages
+ *returns 0 on success
+ *
+ */
+int iperf_tcp_accept(struct iperf_test *);
+
+/**
+ * iperf_tcp_recv -- receives the data for TCP
+ * and the Param/result message exchange
+ *returns state of packet received
+ *
+ */
+int iperf_tcp_recv(struct iperf_stream *);
+
+
+/**
+ * iperf_tcp_send -- sends the client data for TCP
+ * and the Param/result message exchanges
+ * returns: bytes sent
+ *
+ */
+int iperf_tcp_send(struct iperf_stream *) /* __attribute__((hot)) */;
+
+
+int iperf_tcp_listen(struct iperf_test *);
+
+int iperf_tcp_connect(struct iperf_test *);
+
+
+#endif
diff --git a/src/iperf_time.c b/src/iperf_time.c
new file mode 100644
index 0000000..a435dd3
--- /dev/null
+++ b/src/iperf_time.c
@@ -0,0 +1,156 @@
+/*
+ * iperf, Copyright (c) 2014-2018, 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.
+ *
+ * If you have questions about your rights to use or distribute this
+ * software, please contact Berkeley Lab's Technology Transfer
+ * Department at TTD@lbl.gov.
+ *
+ * NOTICE. This software is owned by the U.S. Department of Energy.
+ * As such, the U.S. Government has been granted for itself and others
+ * acting on its behalf a paid-up, nonexclusive, irrevocable,
+ * worldwide license in the Software to reproduce, prepare derivative
+ * works, and perform publicly and display publicly. Beginning five
+ * (5) years after the date permission to assert copyright is obtained
+ * from the U.S. Department of Energy, and subject to any subsequent
+ * five (5) year renewals, the U.S. Government is granted for itself
+ * and others acting on its behalf a paid-up, nonexclusive,
+ * irrevocable, worldwide license in the Software to reproduce,
+ * prepare derivative works, distribute copies to the public, perform
+ * publicly and display publicly, and to permit others to do so.
+ *
+ * This code is distributed under a BSD style license, see the LICENSE
+ * file for complete information.
+ */
+
+
+#include <stddef.h>
+
+#include "iperf_config.h"
+#include "iperf_time.h"
+
+#ifdef HAVE_CLOCK_GETTIME
+
+#include <time.h>
+
+int
+iperf_time_now(struct iperf_time *time1)
+{
+ struct timespec ts;
+ int result;
+ result = clock_gettime(CLOCK_MONOTONIC, &ts);
+ if (result == 0) {
+ time1->secs = (uint32_t) ts.tv_sec;
+ time1->usecs = (uint32_t) ts.tv_nsec / 1000;
+ }
+ return result;
+}
+
+#else
+
+#include <sys/time.h>
+
+int
+iperf_time_now(struct iperf_time *time1)
+{
+ struct timeval tv;
+ int result;
+ result = gettimeofday(&tv, NULL);
+ time1->secs = tv.tv_sec;
+ time1->usecs = tv.tv_usec;
+ return result;
+}
+
+#endif
+
+/* iperf_time_add_usecs
+ *
+ * Add a number of microseconds to a iperf_time.
+ */
+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
+iperf_time_in_usecs(struct iperf_time *time)
+{
+ return time->secs * 1000000LL + time->usecs;
+}
+
+double
+iperf_time_in_secs(struct iperf_time *time)
+{
+ return time->secs + time->usecs / 1000000.0;
+}
+
+/* iperf_time_compare
+ *
+ * Compare two timestamps
+ *
+ * Returns -1 if time1 is earlier, 1 if time1 is later,
+ * or 0 if the timestamps are equal.
+ */
+int
+iperf_time_compare(struct iperf_time *time1, struct iperf_time *time2)
+{
+ if (time1->secs < time2->secs)
+ return -1;
+ if (time1->secs > time2->secs)
+ return 1;
+ if (time1->usecs < time2->usecs)
+ return -1;
+ if (time1->usecs > time2->usecs)
+ return 1;
+ return 0;
+}
+
+/* iperf_time_diff
+ *
+ * Calculates the time from time2 to time1, assuming time1 is later than time2.
+ * The diff will always be positive, so the return value should be checked
+ * to determine if time1 was earlier than time2.
+ *
+ * Returns 1 if the time1 is less than or equal to time2, otherwise 0.
+ */
+int
+iperf_time_diff(struct iperf_time *time1, struct iperf_time *time2, struct iperf_time *diff)
+{
+ int past = 0;
+ int cmp = 0;
+
+ cmp = iperf_time_compare(time1, time2);
+ if (cmp == 0) {
+ diff->secs = 0;
+ diff->usecs = 0;
+ past = 1;
+ }
+ else if (cmp == 1) {
+ diff->secs = time1->secs - time2->secs;
+ diff->usecs = time1->usecs;
+ if (diff->usecs < time2->usecs) {
+ diff->secs -= 1;
+ diff->usecs += 1000000;
+ }
+ diff->usecs = diff->usecs - time2->usecs;
+ } else {
+ diff->secs = time2->secs - time1->secs;
+ diff->usecs = time2->usecs;
+ if (diff->usecs < time1->usecs) {
+ diff->secs -= 1;
+ diff->usecs += 1000000;
+ }
+ diff->usecs = diff->usecs - time1->usecs;
+ past = 1;
+ }
+
+ return past;
+}
diff --git a/src/iperf_time.h b/src/iperf_time.h
new file mode 100644
index 0000000..588ee26
--- /dev/null
+++ b/src/iperf_time.h
@@ -0,0 +1,49 @@
+/*
+ * iperf, Copyright (c) 2014-2018, 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.
+ *
+ * If you have questions about your rights to use or distribute this
+ * software, please contact Berkeley Lab's Technology Transfer
+ * Department at TTD@lbl.gov.
+ *
+ * NOTICE. This software is owned by the U.S. Department of Energy.
+ * As such, the U.S. Government has been granted for itself and others
+ * acting on its behalf a paid-up, nonexclusive, irrevocable,
+ * worldwide license in the Software to reproduce, prepare derivative
+ * works, and perform publicly and display publicly. Beginning five
+ * (5) years after the date permission to assert copyright is obtained
+ * from the U.S. Department of Energy, and subject to any subsequent
+ * five (5) year renewals, the U.S. Government is granted for itself
+ * and others acting on its behalf a paid-up, nonexclusive,
+ * irrevocable, worldwide license in the Software to reproduce,
+ * prepare derivative works, distribute copies to the public, perform
+ * publicly and display publicly, and to permit others to do so.
+ *
+ * This code is distributed under a BSD style license, see the LICENSE
+ * file for complete information.
+ */
+#ifndef __IPERF_TIME_H
+#define __IPERF_TIME_H
+
+#include <stdint.h>
+
+struct iperf_time {
+ uint32_t secs;
+ uint32_t usecs;
+};
+
+int iperf_time_now(struct iperf_time *time1);
+
+void iperf_time_add_usecs(struct iperf_time *time1, uint64_t usecs);
+
+int iperf_time_compare(struct iperf_time *time1, struct iperf_time *time2);
+
+int iperf_time_diff(struct iperf_time *time1, struct iperf_time *time2, struct iperf_time *diff);
+
+uint64_t iperf_time_in_usecs(struct iperf_time *time);
+
+double iperf_time_in_secs(struct iperf_time *time);
+
+#endif
diff --git a/src/iperf_udp.c b/src/iperf_udp.c
new file mode 100644
index 0000000..b7aa1eb
--- /dev/null
+++ b/src/iperf_udp.c
@@ -0,0 +1,635 @@
+/*
+ * iperf, Copyright (c) 2014-2022, 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.
+ *
+ * If you have questions about your rights to use or distribute this
+ * software, please contact Berkeley Lab's Technology Transfer
+ * Department at TTD@lbl.gov.
+ *
+ * NOTICE. This software is owned by the U.S. Department of Energy.
+ * As such, the U.S. Government has been granted for itself and others
+ * acting on its behalf a paid-up, nonexclusive, irrevocable,
+ * worldwide license in the Software to reproduce, prepare derivative
+ * works, and perform publicly and display publicly. Beginning five
+ * (5) years after the date permission to assert copyright is obtained
+ * from the U.S. Department of Energy, and subject to any subsequent
+ * five (5) year renewals, the U.S. Government is granted for itself
+ * and others acting on its behalf a paid-up, nonexclusive,
+ * irrevocable, worldwide license in the Software to reproduce,
+ * prepare derivative works, distribute copies to the public, perform
+ * publicly and display publicly, and to permit others to do so.
+ *
+ * This code is distributed under a BSD style license, see the LICENSE
+ * file for complete information.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <assert.h>
+#include <arpa/inet.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif
+#include <sys/time.h>
+#include <sys/select.h>
+
+#include "iperf.h"
+#include "iperf_api.h"
+#include "iperf_util.h"
+#include "iperf_udp.h"
+#include "timer.h"
+#include "net.h"
+#include "cjson.h"
+#include "portable_endian.h"
+
+#if defined(HAVE_INTTYPES_H)
+# include <inttypes.h>
+#else
+# ifndef PRIu64
+# if sizeof(long) == 8
+# define PRIu64 "lu"
+# else
+# define PRIu64 "llu"
+# endif
+# endif
+#endif
+
+/* iperf_udp_recv
+ *
+ * receives the data for UDP
+ */
+int
+iperf_udp_recv(struct iperf_stream *sp)
+{
+ uint32_t sec, usec;
+ uint64_t pcount;
+ int r;
+ int size = sp->settings->blksize;
+ int first_packet = 0;
+ double transit = 0, d = 0;
+ struct iperf_time sent_time, arrival_time, temp_time;
+
+ r = Nread(sp->socket, sp->buffer, size, Pudp);
+
+ /*
+ * If we got an error in the read, or if we didn't read anything
+ * because the underlying read(2) got a EAGAIN, then skip packet
+ * processing.
+ */
+ if (r <= 0)
+ return r;
+
+ /* Only count bytes received while we're in the correct state. */
+ if (sp->test->state == TEST_RUNNING) {
+
+ /*
+ * For jitter computation below, it's important to know if this
+ * packet is the first packet received.
+ */
+ if (sp->result->bytes_received == 0) {
+ first_packet = 1;
+ }
+
+ sp->result->bytes_received += r;
+ sp->result->bytes_received_this_interval += r;
+
+ /* Dig the various counters out of the incoming UDP packet */
+ if (sp->test->udp_counters_64bit) {
+ memcpy(&sec, sp->buffer, sizeof(sec));
+ memcpy(&usec, sp->buffer+4, sizeof(usec));
+ memcpy(&pcount, sp->buffer+8, sizeof(pcount));
+ sec = ntohl(sec);
+ usec = ntohl(usec);
+ pcount = be64toh(pcount);
+ sent_time.secs = sec;
+ sent_time.usecs = usec;
+ }
+ else {
+ uint32_t pc;
+ memcpy(&sec, sp->buffer, sizeof(sec));
+ memcpy(&usec, sp->buffer+4, sizeof(usec));
+ memcpy(&pc, sp->buffer+8, sizeof(pc));
+ sec = ntohl(sec);
+ usec = ntohl(usec);
+ pcount = ntohl(pc);
+ sent_time.secs = sec;
+ sent_time.usecs = usec;
+ }
+
+ if (sp->test->debug_level >= DEBUG_LEVEL_DEBUG)
+ fprintf(stderr, "pcount %" PRIu64 " packet_count %" PRIu64 "\n", pcount, sp->packet_count);
+
+ /*
+ * Try to handle out of order packets. The way we do this
+ * uses a constant amount of storage but might not be
+ * correct in all cases. In particular we seem to have the
+ * assumption that packets can't be duplicated in the network,
+ * because duplicate packets will possibly cause some problems here.
+ *
+ * First figure out if the sequence numbers are going forward.
+ * Note that pcount is the sequence number read from the packet,
+ * and sp->packet_count is the highest sequence number seen so
+ * far (so we're expecting to see the packet with sequence number
+ * sp->packet_count + 1 arrive next).
+ */
+ if (pcount >= sp->packet_count + 1) {
+
+ /* Forward, but is there a gap in sequence numbers? */
+ if (pcount > sp->packet_count + 1) {
+ /* There's a gap so count that as a loss. */
+ sp->cnt_error += (pcount - 1) - sp->packet_count;
+ }
+ /* Update the highest sequence number seen so far. */
+ sp->packet_count = pcount;
+ } else {
+
+ /*
+ * Sequence number went backward (or was stationary?!?).
+ * This counts as an out-of-order packet.
+ */
+ sp->outoforder_packets++;
+
+ /*
+ * If we have lost packets, then the fact that we are now
+ * seeing an out-of-order packet offsets a prior sequence
+ * number gap that was counted as a loss. So we can take
+ * away a loss.
+ */
+ if (sp->cnt_error > 0)
+ sp->cnt_error--;
+
+ /* Log the out-of-order packet */
+ if (sp->test->debug)
+ fprintf(stderr, "OUT OF ORDER - incoming packet sequence %" PRIu64 " but expected sequence %" PRIu64 " on stream %d", pcount, sp->packet_count + 1, sp->socket);
+ }
+
+ /*
+ * jitter measurement
+ *
+ * This computation is based on RFC 1889 (specifically
+ * sections 6.3.1 and A.8).
+ *
+ * Note that synchronized clocks are not required since
+ * the source packet delta times are known. Also this
+ * computation does not require knowing the round-trip
+ * time.
+ */
+ iperf_time_now(&arrival_time);
+
+ iperf_time_diff(&arrival_time, &sent_time, &temp_time);
+ transit = iperf_time_in_secs(&temp_time);
+
+ /* Hack to handle the first packet by initializing prev_transit. */
+ if (first_packet)
+ sp->prev_transit = transit;
+
+ d = transit - sp->prev_transit;
+ if (d < 0)
+ d = -d;
+ sp->prev_transit = transit;
+ sp->jitter += (d - sp->jitter) / 16.0;
+ }
+ else {
+ if (sp->test->debug)
+ printf("Late receive, state = %d\n", sp->test->state);
+ }
+
+ return r;
+}
+
+
+/* iperf_udp_send
+ *
+ * sends the data for UDP
+ */
+int
+iperf_udp_send(struct iperf_stream *sp)
+{
+ int r;
+ int size = sp->settings->blksize;
+ struct iperf_time before;
+
+ iperf_time_now(&before);
+
+ ++sp->packet_count;
+
+ if (sp->test->udp_counters_64bit) {
+
+ uint32_t sec, usec;
+ uint64_t pcount;
+
+ sec = htonl(before.secs);
+ usec = htonl(before.usecs);
+ pcount = htobe64(sp->packet_count);
+
+ memcpy(sp->buffer, &sec, sizeof(sec));
+ memcpy(sp->buffer+4, &usec, sizeof(usec));
+ memcpy(sp->buffer+8, &pcount, sizeof(pcount));
+
+ }
+ else {
+
+ uint32_t sec, usec, pcount;
+
+ sec = htonl(before.secs);
+ usec = htonl(before.usecs);
+ pcount = htonl(sp->packet_count);
+
+ memcpy(sp->buffer, &sec, sizeof(sec));
+ memcpy(sp->buffer+4, &usec, sizeof(usec));
+ memcpy(sp->buffer+8, &pcount, sizeof(pcount));
+
+ }
+
+ r = Nwrite(sp->socket, sp->buffer, size, Pudp);
+
+ if (r <= 0) {
+ --sp->packet_count; /* Don't count messages that no data was sent from them.
+ * Allows "resending" a massage with the same numbering */
+ if (r < 0) {
+ if (r == NET_SOFTERROR && sp->test->debug_level >= DEBUG_LEVEL_INFO)
+ printf("UDP send failed on NET_SOFTERROR. errno=%s\n", strerror(errno));
+ return r;
+ }
+ }
+
+ sp->result->bytes_sent += r;
+ sp->result->bytes_sent_this_interval += r;
+
+ if (sp->test->debug_level >= DEBUG_LEVEL_DEBUG)
+ printf("sent %d bytes of %d, total %" PRIu64 "\n", r, sp->settings->blksize, sp->result->bytes_sent);
+
+ return r;
+}
+
+
+/**************************************************************************/
+
+/*
+ * The following functions all have to do with managing UDP data sockets.
+ * UDP of course is connectionless, so there isn't really a concept of
+ * setting up a connection, although connect(2) can (and is) used to
+ * bind the remote end of sockets. We need to simulate some of the
+ * connection management that is built-in to TCP so that each side of the
+ * connection knows about each other before the real data transfers begin.
+ */
+
+/*
+ * Set and verify socket buffer sizes.
+ * Return 0 if no error, -1 if an error, +1 if socket buffers are
+ * potentially too small to hold a message.
+ */
+int
+iperf_udp_buffercheck(struct iperf_test *test, int s)
+{
+ int rc = 0;
+ int sndbuf_actual, rcvbuf_actual;
+
+ /*
+ * Set socket buffer size if requested. Do this for both sending and
+ * receiving so that we can cover both normal and --reverse operation.
+ */
+ int opt;
+ socklen_t optlen;
+
+ if ((opt = test->settings->socket_bufsize)) {
+ if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt)) < 0) {
+ i_errno = IESETBUF;
+ return -1;
+ }
+ if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, &opt, sizeof(opt)) < 0) {
+ i_errno = IESETBUF;
+ return -1;
+ }
+ }
+
+ /* Read back and verify the sender socket buffer size */
+ optlen = sizeof(sndbuf_actual);
+ if (getsockopt(s, SOL_SOCKET, SO_SNDBUF, &sndbuf_actual, &optlen) < 0) {
+ i_errno = IESETBUF;
+ return -1;
+ }
+ if (test->debug) {
+ printf("SNDBUF is %u, expecting %u\n", sndbuf_actual, test->settings->socket_bufsize);
+ }
+ if (test->settings->socket_bufsize && test->settings->socket_bufsize > sndbuf_actual) {
+ i_errno = IESETBUF2;
+ return -1;
+ }
+ if (test->settings->blksize > sndbuf_actual) {
+ char str[WARN_STR_LEN];
+ snprintf(str, sizeof(str),
+ "Block size %d > sending socket buffer size %d",
+ test->settings->blksize, sndbuf_actual);
+ warning(str);
+ rc = 1;
+ }
+
+ /* Read back and verify the receiver socket buffer size */
+ optlen = sizeof(rcvbuf_actual);
+ if (getsockopt(s, SOL_SOCKET, SO_RCVBUF, &rcvbuf_actual, &optlen) < 0) {
+ i_errno = IESETBUF;
+ return -1;
+ }
+ if (test->debug) {
+ printf("RCVBUF is %u, expecting %u\n", rcvbuf_actual, test->settings->socket_bufsize);
+ }
+ if (test->settings->socket_bufsize && test->settings->socket_bufsize > rcvbuf_actual) {
+ i_errno = IESETBUF2;
+ return -1;
+ }
+ if (test->settings->blksize > rcvbuf_actual) {
+ char str[WARN_STR_LEN];
+ snprintf(str, sizeof(str),
+ "Block size %d > receiving socket buffer size %d",
+ test->settings->blksize, rcvbuf_actual);
+ warning(str);
+ rc = 1;
+ }
+
+ if (test->json_output) {
+ cJSON *sock_bufsize_item = cJSON_GetObjectItem(test->json_start, "sock_bufsize");
+ if (sock_bufsize_item == NULL) {
+ cJSON_AddNumberToObject(test->json_start, "sock_bufsize", test->settings->socket_bufsize);
+ }
+
+ cJSON *sndbuf_actual_item = cJSON_GetObjectItem(test->json_start, "sndbuf_actual");
+ if (sndbuf_actual_item == NULL) {
+ cJSON_AddNumberToObject(test->json_start, "sndbuf_actual", sndbuf_actual);
+ }
+
+ cJSON *rcvbuf_actual_item = cJSON_GetObjectItem(test->json_start, "rcvbuf_actual");
+ if (rcvbuf_actual_item == NULL) {
+ cJSON_AddNumberToObject(test->json_start, "rcvbuf_actual", rcvbuf_actual);
+ }
+ }
+
+ return rc;
+}
+
+/*
+ * iperf_udp_accept
+ *
+ * Accepts a new UDP "connection"
+ */
+int
+iperf_udp_accept(struct iperf_test *test)
+{
+ struct sockaddr_storage sa_peer;
+ unsigned int buf;
+ socklen_t len;
+ int sz, s;
+ int rc;
+
+ /*
+ * Get the current outstanding socket. This socket will be used to handle
+ * data transfers and a new "listening" socket will be created.
+ */
+ s = test->prot_listener;
+
+ /*
+ * Grab the UDP packet sent by the client. From that we can extract the
+ * client's address, and then use that information to bind the remote side
+ * of the socket to the client.
+ */
+ len = sizeof(sa_peer);
+ if ((sz = recvfrom(test->prot_listener, &buf, sizeof(buf), 0, (struct sockaddr *) &sa_peer, &len)) < 0) {
+ i_errno = IESTREAMACCEPT;
+ return -1;
+ }
+
+ if (connect(s, (struct sockaddr *) &sa_peer, len) < 0) {
+ i_errno = IESTREAMACCEPT;
+ return -1;
+ }
+
+ /* Check and set socket buffer sizes */
+ rc = iperf_udp_buffercheck(test, s);
+ if (rc < 0)
+ /* error */
+ return rc;
+ /*
+ * If the socket buffer was too small, but it was the default
+ * size, then try explicitly setting it to something larger.
+ */
+ if (rc > 0) {
+ if (test->settings->socket_bufsize == 0) {
+ char str[WARN_STR_LEN];
+ int bufsize = test->settings->blksize + UDP_BUFFER_EXTRA;
+ snprintf(str, sizeof(str), "Increasing socket buffer size to %d",
+ bufsize);
+ warning(str);
+ test->settings->socket_bufsize = bufsize;
+ rc = iperf_udp_buffercheck(test, s);
+ if (rc < 0)
+ return rc;
+ }
+ }
+
+#if defined(HAVE_SO_MAX_PACING_RATE)
+ /* If socket pacing is specified, try 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) {
+ if (test->debug) {
+ printf("Setting application pacing to %u\n", rate);
+ }
+ }
+ }
+
+ /*
+ * Create a new "listening" socket to replace the one we were using before.
+ */
+ test->prot_listener = netannounce(test->settings->domain, Pudp, test->bind_address, test->bind_dev, test->server_port);
+ if (test->prot_listener < 0) {
+ i_errno = IESTREAMLISTEN;
+ return -1;
+ }
+
+ FD_SET(test->prot_listener, &test->read_set);
+ test->max_fd = (test->max_fd < test->prot_listener) ? test->prot_listener : test->max_fd;
+
+ /* Let the client know we're ready "accept" another UDP "stream" */
+ buf = UDP_CONNECT_REPLY;
+ if (write(s, &buf, sizeof(buf)) < 0) {
+ i_errno = IESTREAMWRITE;
+ return -1;
+ }
+
+ return s;
+}
+
+
+/*
+ * iperf_udp_listen
+ *
+ * Start up a listener for UDP stream connections. Unlike for TCP,
+ * there is no listen(2) for UDP. This socket will however accept
+ * a UDP datagram from a client (indicating the client's presence).
+ */
+int
+iperf_udp_listen(struct iperf_test *test)
+{
+ int s;
+
+ if ((s = netannounce(test->settings->domain, Pudp, test->bind_address, test->bind_dev, test->server_port)) < 0) {
+ i_errno = IESTREAMLISTEN;
+ return -1;
+ }
+
+ /*
+ * The caller will put this value into test->prot_listener.
+ */
+ return s;
+}
+
+
+/*
+ * iperf_udp_connect
+ *
+ * "Connect" to a UDP stream listener.
+ */
+int
+iperf_udp_connect(struct iperf_test *test)
+{
+ int s, sz;
+ unsigned int buf;
+#ifdef SO_RCVTIMEO
+ struct timeval tv;
+#endif
+ int rc;
+ int i, max_len_wait_for_reply;
+
+ /* Create and bind our local socket. */
+ if ((s = netdial(test->settings->domain, Pudp, test->bind_address, test->bind_dev, test->bind_port, test->server_hostname, test->server_port, -1)) < 0) {
+ i_errno = IESTREAMCONNECT;
+ return -1;
+ }
+
+ /* Check and set socket buffer sizes */
+ rc = iperf_udp_buffercheck(test, s);
+ if (rc < 0)
+ /* error */
+ return rc;
+ /*
+ * If the socket buffer was too small, but it was the default
+ * size, then try explicitly setting it to something larger.
+ */
+ if (rc > 0) {
+ if (test->settings->socket_bufsize == 0) {
+ char str[WARN_STR_LEN];
+ int bufsize = test->settings->blksize + UDP_BUFFER_EXTRA;
+ snprintf(str, sizeof(str), "Increasing socket buffer size to %d",
+ bufsize);
+ warning(str);
+ test->settings->socket_bufsize = bufsize;
+ rc = iperf_udp_buffercheck(test, s);
+ if (rc < 0)
+ return rc;
+ }
+ }
+
+#if defined(HAVE_SO_MAX_PACING_RATE)
+ /* If socket pacing is available and not disabled, try 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) {
+ if (test->debug) {
+ printf("Setting application pacing to %u\n", rate);
+ }
+ }
+ }
+
+ /* Set common socket options */
+ iperf_common_sockopts(test, s);
+
+#ifdef SO_RCVTIMEO
+ /* 30 sec timeout for a case when there is a network problem. */
+ tv.tv_sec = 30;
+ tv.tv_usec = 0;
+ setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (struct timeval *)&tv, sizeof(struct timeval));
+#endif
+
+ /*
+ * Write a datagram to the UDP stream to let the server know we're here.
+ * The server learns our address by obtaining its peer's address.
+ */
+ buf = UDP_CONNECT_MSG;
+ if (test->debug) {
+ printf("Sending Connect message to Socket %d\n", s);
+ }
+ if (write(s, &buf, sizeof(buf)) < 0) {
+ // XXX: Should this be changed to IESTREAMCONNECT?
+ i_errno = IESTREAMWRITE;
+ return -1;
+ }
+
+ /*
+ * Wait until the server replies back to us with the "accept" response.
+ */
+ i = 0;
+ max_len_wait_for_reply = sizeof(buf);
+ if (test->reverse) /* In reverse mode allow few packets to have the "accept" response - to handle out of order packets */
+ max_len_wait_for_reply += MAX_REVERSE_OUT_OF_ORDER_PACKETS * test->settings->blksize;
+ do {
+ if ((sz = recv(s, &buf, sizeof(buf), 0)) < 0) {
+ i_errno = IESTREAMREAD;
+ return -1;
+ }
+ if (test->debug) {
+ printf("Connect received for Socket %d, sz=%d, buf=%x, i=%d, max_len_wait_for_reply=%d\n", s, sz, buf, i, max_len_wait_for_reply);
+ }
+ i += sz;
+ } while (buf != UDP_CONNECT_REPLY && buf != LEGACY_UDP_CONNECT_REPLY && i < max_len_wait_for_reply);
+
+ if (buf != UDP_CONNECT_REPLY && buf != LEGACY_UDP_CONNECT_REPLY) {
+ i_errno = IESTREAMREAD;
+ return -1;
+ }
+
+ return s;
+}
+
+
+/* iperf_udp_init
+ *
+ * initializer for UDP streams in TEST_START
+ */
+int
+iperf_udp_init(struct iperf_test *test)
+{
+ return 0;
+}
diff --git a/src/iperf_udp.h b/src/iperf_udp.h
new file mode 100644
index 0000000..c6f15ab
--- /dev/null
+++ b/src/iperf_udp.h
@@ -0,0 +1,64 @@
+/*
+ * iperf, Copyright (c) 2014, 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.
+ *
+ * If you have questions about your rights to use or distribute this
+ * software, please contact Berkeley Lab's Technology Transfer
+ * Department at TTD@lbl.gov.
+ *
+ * NOTICE. This software is owned by the U.S. Department of Energy.
+ * As such, the U.S. Government has been granted for itself and others
+ * acting on its behalf a paid-up, nonexclusive, irrevocable,
+ * worldwide license in the Software to reproduce, prepare derivative
+ * works, and perform publicly and display publicly. Beginning five
+ * (5) years after the date permission to assert copyright is obtained
+ * from the U.S. Department of Energy, and subject to any subsequent
+ * five (5) year renewals, the U.S. Government is granted for itself
+ * and others acting on its behalf a paid-up, nonexclusive,
+ * irrevocable, worldwide license in the Software to reproduce,
+ * prepare derivative works, distribute copies to the public, perform
+ * publicly and display publicly, and to permit others to do so.
+ *
+ * This code is distributed under a BSD style license, see the LICENSE
+ * file for complete information.
+ */
+#ifndef __IPERF_UDP_H
+#define __IPERF_UDP_H
+
+
+/**
+ * iperf_udp_recv -- receives the client data for UDP
+ *
+ *returns state of packet received
+ *
+ */
+int iperf_udp_recv(struct iperf_stream *);
+
+/**
+ * iperf_udp_send -- sends the client data for UDP
+ *
+ * returns: bytes sent
+ *
+ */
+int iperf_udp_send(struct iperf_stream *) /* __attribute__((hot)) */;
+
+
+/**
+ * iperf_udp_accept -- accepts a new UDP connection
+ * on udp_listener_socket
+ *returns 0 on success
+ *
+ */
+int iperf_udp_accept(struct iperf_test *);
+
+
+int iperf_udp_listen(struct iperf_test *);
+
+int iperf_udp_connect(struct iperf_test *);
+
+int iperf_udp_init(struct iperf_test *);
+
+
+#endif
diff --git a/src/iperf_util.c b/src/iperf_util.c
new file mode 100644
index 0000000..81e8da1
--- /dev/null
+++ b/src/iperf_util.c
@@ -0,0 +1,597 @@
+/*
+ * iperf, Copyright (c) 2014, 2016, 2017, 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.
+ *
+ * If you have questions about your rights to use or distribute this
+ * software, please contact Berkeley Lab's Technology Transfer
+ * Department at TTD@lbl.gov.
+ *
+ * NOTICE. This software is owned by the U.S. Department of Energy.
+ * As such, the U.S. Government has been granted for itself and others
+ * acting on its behalf a paid-up, nonexclusive, irrevocable,
+ * worldwide license in the Software to reproduce, prepare derivative
+ * works, and perform publicly and display publicly. Beginning five
+ * (5) years after the date permission to assert copyright is obtained
+ * from the U.S. Department of Energy, and subject to any subsequent
+ * five (5) year renewals, the U.S. Government is granted for itself
+ * and others acting on its behalf a paid-up, nonexclusive,
+ * irrevocable, worldwide license in the Software to reproduce,
+ * prepare derivative works, distribute copies to the public, perform
+ * publicly and display publicly, and to permit others to do so.
+ *
+ * This code is distributed under a BSD style license, see the LICENSE
+ * file for complete information.
+ */
+/* iperf_util.c
+ *
+ * Iperf utility functions
+ *
+ */
+#include "iperf_config.h"
+
+#include <stdio.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdarg.h>
+#include <sys/select.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/utsname.h>
+#include <time.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include "cjson.h"
+#include "iperf.h"
+#include "iperf_api.h"
+
+/*
+ * Read entropy from /dev/urandom
+ * Errors are fatal.
+ * Returns 0 on success.
+ */
+int readentropy(void *out, size_t outsize)
+{
+ static FILE *frandom;
+ static const char rndfile[] = "/dev/urandom";
+
+ if (!outsize) return 0;
+
+ if (frandom == NULL) {
+ frandom = fopen(rndfile, "rb");
+ if (frandom == NULL) {
+ iperf_errexit(NULL, "error - failed to open %s: %s\n",
+ rndfile, strerror(errno));
+ }
+ setbuf(frandom, NULL);
+ }
+ if (fread(out, 1, outsize, frandom) != outsize) {
+ iperf_errexit(NULL, "error - failed to read %s: %s\n",
+ rndfile,
+ feof(frandom) ? "EOF" : strerror(errno));
+ }
+ return 0;
+}
+
+
+/*
+ * Fills buffer with repeating pattern (similar to pattern that used in iperf2)
+ */
+void fill_with_repeating_pattern(void *out, size_t outsize)
+{
+ size_t i;
+ int counter = 0;
+ char *buf = (char *)out;
+
+ if (!outsize) return;
+
+ for (i = 0; i < outsize; i++) {
+ buf[i] = (char)('0' + counter);
+ if (counter >= 9)
+ counter = 0;
+ else
+ counter++;
+ }
+}
+
+
+/* make_cookie
+ *
+ * Generate and return a cookie string
+ *
+ * Iperf uses this function to create test "cookies" which
+ * server as unique test identifiers. These cookies are also
+ * used for the authentication of stream connections.
+ * Assumes cookie has size (COOKIE_SIZE + 1) char's.
+ */
+
+void
+make_cookie(const char *cookie)
+{
+ unsigned char *out = (unsigned char*)cookie;
+ size_t pos;
+ static const unsigned char rndchars[] = "abcdefghijklmnopqrstuvwxyz234567";
+
+ readentropy(out, COOKIE_SIZE);
+ for (pos = 0; pos < (COOKIE_SIZE - 1); pos++) {
+ out[pos] = rndchars[out[pos] % (sizeof(rndchars) - 1)];
+ }
+ out[pos] = '\0';
+}
+
+
+/* is_closed
+ *
+ * Test if the file descriptor fd is closed.
+ *
+ * Iperf uses this function to test whether a TCP stream socket
+ * is closed, because accepting and denying an invalid connection
+ * in iperf_tcp_accept is not considered an error.
+ */
+
+int
+is_closed(int fd)
+{
+ struct timeval tv;
+ fd_set readset;
+
+ FD_ZERO(&readset);
+ FD_SET(fd, &readset);
+ tv.tv_sec = 0;
+ tv.tv_usec = 0;
+
+ if (select(fd+1, &readset, NULL, NULL, &tv) < 0) {
+ if (errno == EBADF)
+ return 1;
+ }
+ return 0;
+}
+
+
+double
+timeval_to_double(struct timeval * tv)
+{
+ double d;
+
+ d = tv->tv_sec + tv->tv_usec / 1000000;
+
+ return d;
+}
+
+int
+timeval_equals(struct timeval * tv0, struct timeval * tv1)
+{
+ if ( tv0->tv_sec == tv1->tv_sec && tv0->tv_usec == tv1->tv_usec )
+ return 1;
+ else
+ return 0;
+}
+
+double
+timeval_diff(struct timeval * tv0, struct timeval * tv1)
+{
+ double time1, time2;
+
+ time1 = tv0->tv_sec + (tv0->tv_usec / 1000000.0);
+ time2 = tv1->tv_sec + (tv1->tv_usec / 1000000.0);
+
+ time1 = time1 - time2;
+ if (time1 < 0)
+ time1 = -time1;
+ return time1;
+}
+
+void
+cpu_util(double pcpu[3])
+{
+ static struct iperf_time last;
+ static clock_t clast;
+ static struct rusage rlast;
+ struct iperf_time now, temp_time;
+ clock_t ctemp;
+ struct rusage rtemp;
+ double timediff;
+ double userdiff;
+ double systemdiff;
+
+ if (pcpu == NULL) {
+ iperf_time_now(&last);
+ clast = clock();
+ getrusage(RUSAGE_SELF, &rlast);
+ return;
+ }
+
+ iperf_time_now(&now);
+ ctemp = clock();
+ getrusage(RUSAGE_SELF, &rtemp);
+
+ iperf_time_diff(&now, &last, &temp_time);
+ timediff = iperf_time_in_usecs(&temp_time);
+
+ userdiff = ((rtemp.ru_utime.tv_sec * 1000000.0 + rtemp.ru_utime.tv_usec) -
+ (rlast.ru_utime.tv_sec * 1000000.0 + rlast.ru_utime.tv_usec));
+ systemdiff = ((rtemp.ru_stime.tv_sec * 1000000.0 + rtemp.ru_stime.tv_usec) -
+ (rlast.ru_stime.tv_sec * 1000000.0 + rlast.ru_stime.tv_usec));
+
+ pcpu[0] = (((ctemp - clast) * 1000000.0 / CLOCKS_PER_SEC) / timediff) * 100;
+ pcpu[1] = (userdiff / timediff) * 100;
+ pcpu[2] = (systemdiff / timediff) * 100;
+}
+
+const char *
+get_system_info(void)
+{
+ static char buf[1024];
+ struct utsname uts;
+
+ memset(buf, 0, 1024);
+ uname(&uts);
+
+ snprintf(buf, sizeof(buf), "%s %s %s %s %s", uts.sysname, uts.nodename,
+ uts.release, uts.version, uts.machine);
+
+ return buf;
+}
+
+
+const char *
+get_optional_features(void)
+{
+ static char features[1024];
+ unsigned int numfeatures = 0;
+
+ snprintf(features, sizeof(features), "Optional features available: ");
+
+#if defined(HAVE_CPU_AFFINITY)
+ if (numfeatures > 0) {
+ strncat(features, ", ",
+ sizeof(features) - strlen(features) - 1);
+ }
+ strncat(features, "CPU affinity setting",
+ sizeof(features) - strlen(features) - 1);
+ numfeatures++;
+#endif /* HAVE_CPU_AFFINITY */
+
+#if defined(HAVE_FLOWLABEL)
+ if (numfeatures > 0) {
+ strncat(features, ", ",
+ sizeof(features) - strlen(features) - 1);
+ }
+ strncat(features, "IPv6 flow label",
+ sizeof(features) - strlen(features) - 1);
+ numfeatures++;
+#endif /* HAVE_FLOWLABEL */
+
+#if defined(HAVE_SCTP_H)
+ if (numfeatures > 0) {
+ strncat(features, ", ",
+ sizeof(features) - strlen(features) - 1);
+ }
+ strncat(features, "SCTP",
+ sizeof(features) - strlen(features) - 1);
+ numfeatures++;
+#endif /* HAVE_SCTP_H */
+
+#if defined(HAVE_TCP_CONGESTION)
+ if (numfeatures > 0) {
+ strncat(features, ", ",
+ sizeof(features) - strlen(features) - 1);
+ }
+ strncat(features, "TCP congestion algorithm setting",
+ sizeof(features) - strlen(features) - 1);
+ numfeatures++;
+#endif /* HAVE_TCP_CONGESTION */
+
+#if defined(HAVE_SENDFILE)
+ if (numfeatures > 0) {
+ strncat(features, ", ",
+ sizeof(features) - strlen(features) - 1);
+ }
+ strncat(features, "sendfile / zerocopy",
+ sizeof(features) - strlen(features) - 1);
+ numfeatures++;
+#endif /* HAVE_SENDFILE */
+
+#if defined(HAVE_SO_MAX_PACING_RATE)
+ if (numfeatures > 0) {
+ strncat(features, ", ",
+ sizeof(features) - strlen(features) - 1);
+ }
+ strncat(features, "socket pacing",
+ sizeof(features) - strlen(features) - 1);
+ numfeatures++;
+#endif /* HAVE_SO_MAX_PACING_RATE */
+
+#if defined(HAVE_SSL)
+ if (numfeatures > 0) {
+ strncat(features, ", ",
+ sizeof(features) - strlen(features) - 1);
+ }
+ strncat(features, "authentication",
+ sizeof(features) - strlen(features) - 1);
+ numfeatures++;
+#endif /* HAVE_SSL */
+
+#if defined(HAVE_SO_BINDTODEVICE)
+ if (numfeatures > 0) {
+ strncat(features, ", ",
+ sizeof(features) - strlen(features) - 1);
+ }
+ strncat(features, "bind to device",
+ sizeof(features) - strlen(features) - 1);
+ numfeatures++;
+#endif /* HAVE_SO_BINDTODEVICE */
+
+#if defined(HAVE_DONT_FRAGMENT)
+ if (numfeatures > 0) {
+ strncat(features, ", ",
+ sizeof(features) - strlen(features) - 1);
+ }
+ strncat(features, "support IPv4 don't fragment",
+ sizeof(features) - strlen(features) - 1);
+ numfeatures++;
+#endif /* HAVE_DONT_FRAGMENT */
+
+#if defined(HAVE_PTHREAD)
+ if (numfeatures > 0) {
+ strncat(features, ", ",
+ sizeof(features) - strlen(features) - 1);
+ }
+ strncat(features, "POSIX threads",
+ sizeof(features) - strlen(features) - 1);
+ numfeatures++;
+#endif /* HAVE_PTHREAD */
+
+ if (numfeatures == 0) {
+ strncat(features, "None",
+ sizeof(features) - strlen(features) - 1);
+ }
+
+ return features;
+}
+
+/* Helper routine for building cJSON objects in a printf-like manner.
+**
+** Sample call:
+** j = iperf_json_printf("foo: %b bar: %d bletch: %f eep: %s", b, i, f, s);
+**
+** The four formatting characters and the types they expect are:
+** %b boolean int
+** %d integer int64_t
+** %f floating point double
+** %s string char *
+** If the values you're passing in are not these exact types, you must
+** cast them, there is no automatic type coercion/widening here.
+**
+** The colons mark the end of field names, and blanks are ignored.
+**
+** This routine is not particularly robust, but it's not part of the API,
+** it's just for internal iperf3 use.
+*/
+cJSON*
+iperf_json_printf(const char *format, ...)
+{
+ cJSON* o;
+ va_list argp;
+ const char *cp;
+ char name[100];
+ char* np;
+ cJSON* j;
+
+ o = cJSON_CreateObject();
+ if (o == NULL)
+ return NULL;
+ va_start(argp, format);
+ np = name;
+ for (cp = format; *cp != '\0'; ++cp) {
+ switch (*cp) {
+ case ' ':
+ break;
+ case ':':
+ *np = '\0';
+ break;
+ case '%':
+ ++cp;
+ switch (*cp) {
+ case 'b':
+ j = cJSON_CreateBool(va_arg(argp, int));
+ break;
+ case 'd':
+ j = cJSON_CreateNumber(va_arg(argp, int64_t));
+ break;
+ case 'f':
+ j = cJSON_CreateNumber(va_arg(argp, double));
+ break;
+ case 's':
+ j = cJSON_CreateString(va_arg(argp, char *));
+ break;
+ default:
+ va_end(argp);
+ return NULL;
+ }
+ if (j == NULL) {
+ va_end(argp);
+ return NULL;
+ }
+ cJSON_AddItemToObject(o, name, j);
+ np = name;
+ break;
+ default:
+ *np++ = *cp;
+ break;
+ }
+ }
+ va_end(argp);
+ return o;
+}
+
+/* Debugging routine to dump out an fd_set. */
+void
+iperf_dump_fdset(FILE *fp, const char *str, int nfds, fd_set *fds)
+{
+ int fd;
+ int comma;
+
+ fprintf(fp, "%s: [", str);
+ comma = 0;
+ for (fd = 0; fd < nfds; ++fd) {
+ if (FD_ISSET(fd, fds)) {
+ if (comma)
+ fprintf(fp, ", ");
+ fprintf(fp, "%d", fd);
+ comma = 1;
+ }
+ }
+ fprintf(fp, "]\n");
+}
+
+/*
+ * daemon(3) implementation for systems lacking one.
+ * Cobbled together from various daemon(3) implementations,
+ * not intended to be general-purpose. */
+#ifndef HAVE_DAEMON
+int daemon(int nochdir, int noclose)
+{
+ pid_t pid = 0;
+ pid_t sid = 0;
+ int fd;
+
+ /*
+ * Ignore any possible SIGHUP when the parent process exits.
+ * Note that the iperf3 server process will eventually install
+ * its own signal handler for SIGHUP, so we can be a little
+ * sloppy about not restoring the prior value. This does not
+ * generalize.
+ */
+ signal(SIGHUP, SIG_IGN);
+
+ pid = fork();
+ if (pid < 0) {
+ return -1;
+ }
+ if (pid > 0) {
+ /* Use _exit() to avoid doing atexit() stuff. */
+ _exit(0);
+ }
+
+ sid = setsid();
+ if (sid < 0) {
+ return -1;
+ }
+
+ /*
+ * Fork again to avoid becoming a session leader.
+ * This might only matter on old SVr4-derived OSs.
+ * Note in particular that glibc and FreeBSD libc
+ * only fork once.
+ */
+ pid = fork();
+ if (pid == -1) {
+ return -1;
+ } else if (pid != 0) {
+ _exit(0);
+ }
+
+ if (!nochdir) {
+ chdir("/");
+ }
+
+ if (!noclose && (fd = open("/dev/null", O_RDWR, 0)) != -1) {
+ dup2(fd, STDIN_FILENO);
+ dup2(fd, STDOUT_FILENO);
+ dup2(fd, STDERR_FILENO);
+ if (fd > 2) {
+ close(fd);
+ }
+ }
+ return (0);
+}
+#endif /* HAVE_DAEMON */
+
+/* Compatibility version of getline(3) for systems that don't have it.. */
+#ifndef HAVE_GETLINE
+/* The following code adopted from NetBSD's getline.c, which is: */
+
+/*-
+ * Copyright (c) 2011 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Christos Zoulas.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+ssize_t
+getdelim(char **buf, size_t *bufsiz, int delimiter, FILE *fp)
+{
+ char *ptr, *eptr;
+
+
+ if (*buf == NULL || *bufsiz == 0) {
+ *bufsiz = BUFSIZ;
+ if ((*buf = malloc(*bufsiz)) == NULL)
+ return -1;
+ }
+
+ for (ptr = *buf, eptr = *buf + *bufsiz;;) {
+ int c = fgetc(fp);
+ if (c == -1) {
+ if (feof(fp)) {
+ ssize_t diff = (ssize_t)(ptr - *buf);
+ if (diff != 0) {
+ *ptr = '\0';
+ return diff;
+ }
+ }
+ return -1;
+ }
+ *ptr++ = c;
+ if (c == delimiter) {
+ *ptr = '\0';
+ return ptr - *buf;
+ }
+ if (ptr + 2 >= eptr) {
+ char *nbuf;
+ size_t nbufsiz = *bufsiz * 2;
+ ssize_t d = ptr - *buf;
+ if ((nbuf = realloc(*buf, nbufsiz)) == NULL)
+ return -1;
+ *buf = nbuf;
+ *bufsiz = nbufsiz;
+ eptr = nbuf + nbufsiz;
+ ptr = nbuf + d;
+ }
+ }
+}
+
+ssize_t
+getline(char **buf, size_t *bufsiz, FILE *fp)
+{
+ return getdelim(buf, bufsiz, '\n', fp);
+}
+
+#endif
diff --git a/src/iperf_util.h b/src/iperf_util.h
new file mode 100644
index 0000000..b109af2
--- /dev/null
+++ b/src/iperf_util.h
@@ -0,0 +1,67 @@
+/*
+ * iperf, Copyright (c) 2014-2017, 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.
+ *
+ * If you have questions about your rights to use or distribute this
+ * software, please contact Berkeley Lab's Technology Transfer
+ * Department at TTD@lbl.gov.
+ *
+ * NOTICE. This software is owned by the U.S. Department of Energy.
+ * As such, the U.S. Government has been granted for itself and others
+ * acting on its behalf a paid-up, nonexclusive, irrevocable,
+ * worldwide license in the Software to reproduce, prepare derivative
+ * works, and perform publicly and display publicly. Beginning five
+ * (5) years after the date permission to assert copyright is obtained
+ * from the U.S. Department of Energy, and subject to any subsequent
+ * five (5) year renewals, the U.S. Government is granted for itself
+ * and others acting on its behalf a paid-up, nonexclusive,
+ * irrevocable, worldwide license in the Software to reproduce,
+ * prepare derivative works, distribute copies to the public, perform
+ * publicly and display publicly, and to permit others to do so.
+ *
+ * This code is distributed under a BSD style license, see the LICENSE
+ * file for complete information.
+ */
+#ifndef __IPERF_UTIL_H
+#define __IPERF_UTIL_H
+
+#include "iperf_config.h"
+#include "cjson.h"
+#include <sys/select.h>
+#include <stddef.h>
+
+int readentropy(void *out, size_t outsize);
+
+void fill_with_repeating_pattern(void *out, size_t outsize);
+
+void make_cookie(char *);
+
+int is_closed(int);
+
+double timeval_to_double(struct timeval *tv);
+
+int timeval_equals(struct timeval *tv0, struct timeval *tv1);
+
+double timeval_diff(struct timeval *tv0, struct timeval *tv1);
+
+void cpu_util(double pcpu[3]);
+
+const char* get_system_info(void);
+
+const char* get_optional_features(void);
+
+cJSON* iperf_json_printf(const char *format, ...);
+
+void iperf_dump_fdset(FILE *fp, const char *str, int nfds, fd_set *fds);
+
+#ifndef HAVE_DAEMON
+extern int daemon(int nochdir, int noclose);
+#endif /* HAVE_DAEMON */
+
+#ifndef HAVE_GETLINE
+ssize_t getline(char **buf, size_t *bufsiz, FILE *fp);
+#endif /* HAVE_GETLINE */
+
+#endif
diff --git a/src/libiperf.3 b/src/libiperf.3
new file mode 100644
index 0000000..4b278e3
--- /dev/null
+++ b/src/libiperf.3
@@ -0,0 +1,114 @@
+.TH LIBIPERF 3 "January 2022" ESnet "User Manuals"
+.SH NAME
+libiperf \- API for iperf3 network throughput tester
+
+.SH SYNOPSIS
+#include <iperf_api.h>
+.br
+\-liperf
+
+.SH DESCRIPTION
+.PP
+Libiperf gives you access to all the functionality of the iperf3
+network testing tool.
+You can build it directly into your own program, instead of having
+to run it as a shell command.
+
+.SH CALLS
+Initialization / termination:
+.nf
+ struct iperf_test *iperf_new_test();
+ int iperf_defaults(struct iperf_test *t);
+ void iperf_free_test(struct iperf_test *t);
+.fi
+Setting test parameters:
+.nf
+ void iperf_set_test_role( struct iperf_test *pt, char role );
+ void iperf_set_test_bind_address( struct iperf_test *t, char *bind_address );
+ void iperf_set_test_bind_dev( struct iperf_test *t, char *bind_dev );
+ void iperf_set_test_server_hostname( struct iperf_test *t, char *server_host );
+ void iperf_set_test_server_port( struct iperf_test *t, int server_port );
+ void iperf_set_test_duration( struct iperf_test *t, int duration );
+ void iperf_set_test_blksize( struct iperf_test *t, int blksize );
+ void iperf_set_test_num_streams( struct iperf_test *t, int num_streams );
+ void iperf_set_test_json_output( struct iperf_test *t, int json_output );
+ int iperf_has_zerocopy( void );
+ void iperf_set_test_zerocopy( struct iperf_test* t, int zerocopy );
+ void iperf_set_test_tos( struct iperf_test* t, int tos );
+.fi
+Authentication functions:
+.nf
+ void iperf_set_test_client_username(struct iperf_test *ipt, char *client_username)
+ void iperf_set_test_client_password(struct iperf_test *ipt, char *client_password)
+ void iperf_set_test_client_rsa_pubkey(struct iperf_test *ipt, char *client_rsa_pubkey_base64)
+.fi
+Running a test:
+.nf
+ int iperf_run_client(struct iperf_test *);
+ int iperf_run_server(struct iperf_test *);
+ void iperf_reset_test(struct iperf_test *);
+.fi
+Output:
+.nf
+ FILE *iperf_get_test_outfile(struct iperf_test *);
+ char* iperf_get_test_json_output_string(struct iperf_test *);
+.fi
+Error reporting:
+.nf
+ void iperf_err(struct iperf_test *t, const char *format, ...);
+ char *iperf_strerror(int);
+ extern int i_errno;
+.fi
+This is not a complete list of the available calls.
+See the include file for more.
+
+.SH EXAMPLES
+Here's some sample code that runs an iperf client:
+.nf
+ struct iperf_test *test;
+ test = iperf_new_test();
+ if ( test == NULL ) {
+ fprintf( stderr, "%s: failed to create test\n", argv0 );
+ exit( EXIT_FAILURE );
+ }
+ iperf_defaults( test );
+ iperf_set_test_role( test, 'c' );
+ iperf_set_test_server_hostname( test, host );
+ iperf_set_test_server_port( test, port );
+ if ( iperf_run_client( test ) < 0 ) {
+ fprintf( stderr, "%s: error - %s\n", argv0, iperf_strerror( i_errno ) );
+ exit( EXIT_FAILURE );
+ }
+ iperf_free_test( test );
+.fi
+And here's a server:
+.nf
+ struct iperf_test *test;
+ test = iperf_new_test();
+ if ( test == NULL ) {
+ fprintf( stderr, "%s: failed to create test\n", argv0 );
+ exit( EXIT_FAILURE );
+ }
+ iperf_defaults( test );
+ iperf_set_test_role( test, 's' );
+ iperf_set_test_server_port( test, port );
+ for (;;) {
+ if ( iperf_run_server( test ) < 0 )
+ fprintf( stderr, "%s: error - %s\n\n", argv0, iperf_strerror( i_errn
+o ) );
+ iperf_reset_test( test );
+ }
+ iperf_free_test( test );
+.fi
+These are not complete programs, just excerpts.
+The full runnable source code can be found in the examples subdirectory
+of the iperf3 source tree.
+
+.SH AUTHORS
+A list of the contributors to iperf3 can be found within the
+documentation located at
+\fChttps://software.es.net/iperf/dev.html#authors\fR.
+
+.SH "SEE ALSO"
+iperf3(1),
+https://software.es.net/iperf/
diff --git a/src/main.c b/src/main.c
new file mode 100644
index 0000000..b179f5b
--- /dev/null
+++ b/src/main.c
@@ -0,0 +1,214 @@
+/*
+ * 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.
+ *
+ * If you have questions about your rights to use or distribute this
+ * software, please contact Berkeley Lab's Technology Transfer
+ * Department at TTD@lbl.gov.
+ *
+ * NOTICE. This software is owned by the U.S. Department of Energy.
+ * As such, the U.S. Government has been granted for itself and others
+ * acting on its behalf a paid-up, nonexclusive, irrevocable,
+ * worldwide license in the Software to reproduce, prepare derivative
+ * works, and perform publicly and display publicly. Beginning five
+ * (5) years after the date permission to assert copyright is obtained
+ * from the U.S. Department of Energy, and subject to any subsequent
+ * five (5) year renewals, the U.S. Government is granted for itself
+ * and others acting on its behalf a paid-up, nonexclusive,
+ * irrevocable, worldwide license in the Software to reproduce,
+ * prepare derivative works, distribute copies to the public, perform
+ * publicly and display publicly, and to permit others to do so.
+ *
+ * This code is distributed under a BSD style license, see the LICENSE
+ * file for complete information.
+ */
+#include "iperf_config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <errno.h>
+#include <signal.h>
+#include <unistd.h>
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include "iperf.h"
+#include "iperf_api.h"
+#include "iperf_util.h"
+#include "iperf_locale.h"
+#include "net.h"
+#include "units.h"
+
+
+static int run(struct iperf_test *test);
+
+
+/**************************************************************************/
+int
+main(int argc, char **argv)
+{
+ struct iperf_test *test;
+
+ /*
+ * Atomics check. We prefer to have atomic types (which is
+ * basically on any compiler supporting C11 or better). If we
+ * don't have them, we try to approximate the type we need with a
+ * regular integer, but complain if they're not lock-free. We only
+ * know how to check this on GCC. GCC on CentOS 7 / RHEL 7 is the
+ * targeted use case for these check.
+ */
+#ifndef HAVE_STDATOMIC_H
+#ifdef __GNUC__
+ if (! __atomic_always_lock_free (sizeof (u_int64_t), 0)) {
+#endif // __GNUC__
+ fprintf(stderr, "Warning: Cannot guarantee lock-free operation with 64-bit data types\n");
+#ifdef __GNUC__
+ }
+#endif // __GNUC__
+#endif // HAVE_STDATOMIC_H
+
+ // XXX: Setting the process affinity requires root on most systems.
+ // Is this a feature we really need?
+#ifdef TEST_PROC_AFFINITY
+ /* didn't seem to work.... */
+ /*
+ * increasing the priority of the process to minimise packet generation
+ * delay
+ */
+ int rc = setpriority(PRIO_PROCESS, 0, -15);
+
+ if (rc < 0) {
+ perror("setpriority:");
+ fprintf(stderr, "setting priority to valid level\n");
+ rc = setpriority(PRIO_PROCESS, 0, 0);
+ }
+
+ /* setting the affinity of the process */
+ cpu_set_t cpu_set;
+ int affinity = -1;
+ int ncores = 1;
+
+ sched_getaffinity(0, sizeof(cpu_set_t), &cpu_set);
+ if (errno)
+ perror("couldn't get affinity:");
+
+ if ((ncores = sysconf(_SC_NPROCESSORS_CONF)) <= 0)
+ err("sysconf: couldn't get _SC_NPROCESSORS_CONF");
+
+ CPU_ZERO(&cpu_set);
+ CPU_SET(affinity, &cpu_set);
+ if (sched_setaffinity(0, sizeof(cpu_set_t), &cpu_set) != 0)
+ err("couldn't change CPU affinity");
+#endif
+
+ test = iperf_new_test();
+ if (!test)
+ iperf_errexit(NULL, "create new test error - %s", iperf_strerror(i_errno));
+ iperf_defaults(test); /* sets defaults */
+
+ if (iperf_parse_arguments(test, argc, argv) < 0) {
+ iperf_err(test, "parameter error - %s", iperf_strerror(i_errno));
+ fprintf(stderr, "\n");
+ usage();
+ exit(1);
+ }
+
+ if (run(test) < 0)
+ iperf_errexit(test, "error - %s", iperf_strerror(i_errno));
+
+ iperf_free_test(test);
+
+ return 0;
+}
+
+
+static jmp_buf sigend_jmp_buf;
+
+static void __attribute__ ((noreturn))
+sigend_handler(int sig)
+{
+ longjmp(sigend_jmp_buf, 1);
+}
+
+/**************************************************************************/
+static int
+run(struct iperf_test *test)
+{
+ /* Termination signals. */
+ iperf_catch_sigend(sigend_handler);
+ if (setjmp(sigend_jmp_buf))
+ iperf_got_sigend(test);
+
+ /* Ignore SIGPIPE to simplify error handling */
+ signal(SIGPIPE, SIG_IGN);
+
+ switch (test->role) {
+ case 's':
+ if (test->daemon) {
+ int rc;
+ rc = daemon(0, 0);
+ if (rc < 0) {
+ i_errno = IEDAEMON;
+ iperf_errexit(test, "error - %s", iperf_strerror(i_errno));
+ }
+ }
+ if (iperf_create_pidfile(test) < 0) {
+ i_errno = IEPIDFILE;
+ iperf_errexit(test, "error - %s", iperf_strerror(i_errno));
+ }
+ for (;;) {
+ int rc;
+ rc = iperf_run_server(test);
+ test->server_last_run_rc = rc;
+ if (rc < 0) {
+ iperf_err(test, "error - %s", iperf_strerror(i_errno));
+ if (test->json_output) {
+ if (iperf_json_finish(test) < 0)
+ return -1;
+ }
+ iflush(test);
+
+ if (rc < -1) {
+ iperf_errexit(test, "exiting");
+ }
+ }
+ iperf_reset_test(test);
+ if (iperf_get_test_one_off(test) && rc != 2) {
+ /* Authentication failure doesn't count for 1-off test */
+ if (rc < 0 && i_errno == IEAUTHTEST) {
+ continue;
+ }
+ break;
+ }
+ }
+ iperf_delete_pidfile(test);
+ break;
+ case 'c':
+ if (iperf_create_pidfile(test) < 0) {
+ i_errno = IEPIDFILE;
+ iperf_errexit(test, "error - %s", iperf_strerror(i_errno));
+ }
+ if (iperf_run_client(test) < 0)
+ iperf_errexit(test, "error - %s", iperf_strerror(i_errno));
+ iperf_delete_pidfile(test);
+ break;
+ default:
+ usage();
+ break;
+ }
+
+ iperf_catch_sigend(SIG_DFL);
+ signal(SIGPIPE, SIG_DFL);
+
+ return 0;
+}
diff --git a/src/net.c b/src/net.c
new file mode 100644
index 0000000..c82caff
--- /dev/null
+++ b/src/net.c
@@ -0,0 +1,607 @@
+/*
+ * 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.
+ *
+ * If you have questions about your rights to use or distribute this
+ * software, please contact Berkeley Lab's Technology Transfer
+ * Department at TTD@lbl.gov.
+ *
+ * NOTICE. This software is owned by the U.S. Department of Energy.
+ * As such, the U.S. Government has been granted for itself and others
+ * acting on its behalf a paid-up, nonexclusive, irrevocable,
+ * worldwide license in the Software to reproduce, prepare derivative
+ * works, and perform publicly and display publicly. Beginning five
+ * (5) years after the date permission to assert copyright is obtained
+ * from the U.S. Department of Energy, and subject to any subsequent
+ * five (5) year renewals, the U.S. Government is granted for itself
+ * and others acting on its behalf a paid-up, nonexclusive,
+ * irrevocable, worldwide license in the Software to reproduce,
+ * prepare derivative works, distribute copies to the public, perform
+ * publicly and display publicly, and to permit others to do so.
+ *
+ * This code is distributed under a BSD style license, see the LICENSE
+ * file for complete information.
+ */
+#include "iperf_config.h"
+
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <arpa/inet.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <assert.h>
+#include <netdb.h>
+#include <string.h>
+#include <fcntl.h>
+#include <limits.h>
+
+#ifdef HAVE_SENDFILE
+#ifdef linux
+#include <sys/sendfile.h>
+#else
+#ifdef __FreeBSD__
+#include <sys/uio.h>
+#else
+#if defined(__APPLE__) && defined(__MACH__) /* OS X */
+#include <AvailabilityMacros.h>
+#if defined(MAC_OS_X_VERSION_10_6)
+#include <sys/uio.h>
+#endif
+#endif
+#endif
+#endif
+#endif /* HAVE_SENDFILE */
+
+#ifdef HAVE_POLL_H
+#include <poll.h>
+#endif /* HAVE_POLL_H */
+
+#include "iperf.h"
+#include "iperf_util.h"
+#include "net.h"
+#include "timer.h"
+
+static int nread_read_timeout = 10;
+static int nread_overall_timeout = 30;
+
+/*
+ * Declaration of gerror in iperf_error.c. Most other files in iperf3 can get this
+ * by including "iperf.h", but net.c lives "below" this layer. Clearly the
+ * presence of this declaration is a sign we need to revisit this layering.
+ */
+extern int gerror;
+
+/*
+ * timeout_connect adapted from netcat, via OpenBSD and FreeBSD
+ * Copyright (c) 2001 Eric Jackson <ericj@monkey.org>
+ */
+int
+timeout_connect(int s, const struct sockaddr *name, socklen_t namelen,
+ int timeout)
+{
+ struct pollfd pfd;
+ socklen_t optlen;
+ int flags, optval;
+ int ret;
+
+ flags = 0;
+ if (timeout != -1) {
+ flags = fcntl(s, F_GETFL, 0);
+ if (fcntl(s, F_SETFL, flags | O_NONBLOCK) == -1)
+ return -1;
+ }
+
+ if ((ret = connect(s, name, namelen)) != 0 && errno == EINPROGRESS) {
+ pfd.fd = s;
+ pfd.events = POLLOUT;
+ if ((ret = poll(&pfd, 1, timeout)) == 1) {
+ optlen = sizeof(optval);
+ if ((ret = getsockopt(s, SOL_SOCKET, SO_ERROR,
+ &optval, &optlen)) == 0) {
+ errno = optval;
+ ret = optval == 0 ? 0 : -1;
+ }
+ } else if (ret == 0) {
+ errno = ETIMEDOUT;
+ ret = -1;
+ } else
+ ret = -1;
+ }
+
+ if (timeout != -1 && fcntl(s, F_SETFL, flags) == -1)
+ ret = -1;
+
+ return (ret);
+}
+
+/* netdial and netannouce code comes from libtask: http://swtch.com/libtask/
+ * Copyright: http://swtch.com/libtask/COPYRIGHT
+*/
+
+/* create a socket */
+int
+create_socket(int domain, int proto, const char *local, const char *bind_dev, int local_port, const char *server, int port, struct addrinfo **server_res_out)
+{
+ struct addrinfo hints, *local_res = NULL, *server_res = NULL;
+ int s, saved_errno;
+ char portstr[6];
+
+ if (local) {
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = domain;
+ hints.ai_socktype = proto;
+ if ((gerror = getaddrinfo(local, NULL, &hints, &local_res)) != 0)
+ return -1;
+ }
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = domain;
+ hints.ai_socktype = proto;
+ snprintf(portstr, sizeof(portstr), "%d", port);
+ if ((gerror = getaddrinfo(server, portstr, &hints, &server_res)) != 0) {
+ if (local)
+ freeaddrinfo(local_res);
+ return -1;
+ }
+
+ s = socket(server_res->ai_family, proto, 0);
+ if (s < 0) {
+ if (local)
+ freeaddrinfo(local_res);
+ freeaddrinfo(server_res);
+ return -1;
+ }
+
+ if (bind_dev) {
+#if defined(HAVE_SO_BINDTODEVICE)
+ if (setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE,
+ bind_dev, IFNAMSIZ) < 0)
+#endif // HAVE_SO_BINDTODEVICE
+ {
+ saved_errno = errno;
+ close(s);
+ freeaddrinfo(local_res);
+ freeaddrinfo(server_res);
+ errno = saved_errno;
+ return -1;
+ }
+ }
+
+ /* Bind the local address if given a name (with or without --cport) */
+ if (local) {
+ if (local_port) {
+ struct sockaddr_in *lcladdr;
+ lcladdr = (struct sockaddr_in *)local_res->ai_addr;
+ lcladdr->sin_port = htons(local_port);
+ }
+
+ if (bind(s, (struct sockaddr *) local_res->ai_addr, local_res->ai_addrlen) < 0) {
+ saved_errno = errno;
+ close(s);
+ freeaddrinfo(local_res);
+ freeaddrinfo(server_res);
+ errno = saved_errno;
+ return -1;
+ }
+ freeaddrinfo(local_res);
+ }
+ /* No local name, but --cport given */
+ else if (local_port) {
+ size_t addrlen;
+ struct sockaddr_storage lcl;
+
+ /* IPv4 */
+ if (server_res->ai_family == AF_INET) {
+ struct sockaddr_in *lcladdr = (struct sockaddr_in *) &lcl;
+ lcladdr->sin_family = AF_INET;
+ lcladdr->sin_port = htons(local_port);
+ lcladdr->sin_addr.s_addr = INADDR_ANY;
+ addrlen = sizeof(struct sockaddr_in);
+ }
+ /* IPv6 */
+ else if (server_res->ai_family == AF_INET6) {
+ struct sockaddr_in6 *lcladdr = (struct sockaddr_in6 *) &lcl;
+ lcladdr->sin6_family = AF_INET6;
+ lcladdr->sin6_port = htons(local_port);
+ lcladdr->sin6_addr = in6addr_any;
+ addrlen = sizeof(struct sockaddr_in6);
+ }
+ /* Unknown protocol */
+ else {
+ close(s);
+ freeaddrinfo(server_res);
+ errno = EAFNOSUPPORT;
+ return -1;
+ }
+
+ if (bind(s, (struct sockaddr *) &lcl, addrlen) < 0) {
+ saved_errno = errno;
+ close(s);
+ freeaddrinfo(server_res);
+ errno = saved_errno;
+ return -1;
+ }
+ }
+
+ *server_res_out = server_res;
+ return s;
+}
+
+/* make connection to server */
+int
+netdial(int domain, int proto, const char *local, const char *bind_dev, int local_port, const char *server, int port, int timeout)
+{
+ struct addrinfo *server_res = NULL;
+ int s, saved_errno;
+
+ s = create_socket(domain, proto, local, bind_dev, local_port, server, port, &server_res);
+ if (s < 0) {
+ return -1;
+ }
+
+ if (timeout_connect(s, (struct sockaddr *) server_res->ai_addr, server_res->ai_addrlen, timeout) < 0 && errno != EINPROGRESS) {
+ saved_errno = errno;
+ close(s);
+ freeaddrinfo(server_res);
+ errno = saved_errno;
+ return -1;
+ }
+
+ freeaddrinfo(server_res);
+ return s;
+}
+
+/***************************************************************/
+
+int
+netannounce(int domain, int proto, const char *local, const char *bind_dev, int port)
+{
+ struct addrinfo hints, *res;
+ char portstr[6];
+ int s, opt, saved_errno;
+
+ snprintf(portstr, 6, "%d", port);
+ memset(&hints, 0, sizeof(hints));
+ /*
+ * If binding to the wildcard address with no explicit address
+ * family specified, then force us to get an AF_INET6 socket. On
+ * CentOS 6 and MacOS, getaddrinfo(3) with AF_UNSPEC in ai_family,
+ * and ai_flags containing AI_PASSIVE returns a result structure
+ * with ai_family set to AF_INET, with the result that we create
+ * and bind an IPv4 address wildcard address and by default, we
+ * can't accept IPv6 connections.
+ *
+ * On FreeBSD, under the above circumstances, ai_family in the
+ * result structure is set to AF_INET6.
+ */
+ if (domain == AF_UNSPEC && !local) {
+ hints.ai_family = AF_INET6;
+ }
+ else {
+ hints.ai_family = domain;
+ }
+ hints.ai_socktype = proto;
+ hints.ai_flags = AI_PASSIVE;
+ if ((gerror = getaddrinfo(local, portstr, &hints, &res)) != 0)
+ return -1;
+
+ s = socket(res->ai_family, proto, 0);
+ if (s < 0) {
+ freeaddrinfo(res);
+ return -1;
+ }
+
+ if (bind_dev) {
+#if defined(HAVE_SO_BINDTODEVICE)
+ if (setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE,
+ bind_dev, IFNAMSIZ) < 0)
+#endif // HAVE_SO_BINDTODEVICE
+ {
+ saved_errno = errno;
+ close(s);
+ freeaddrinfo(res);
+ errno = saved_errno;
+ return -1;
+ }
+ }
+
+ opt = 1;
+ if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
+ (char *) &opt, sizeof(opt)) < 0) {
+ saved_errno = errno;
+ close(s);
+ freeaddrinfo(res);
+ errno = saved_errno;
+ return -1;
+ }
+ /*
+ * If we got an IPv6 socket, figure out if it should accept IPv4
+ * connections as well. We do that if and only if no address
+ * family was specified explicitly. Note that we can only
+ * do this if the IPV6_V6ONLY socket option is supported. Also,
+ * OpenBSD explicitly omits support for IPv4-mapped addresses,
+ * even though it implements IPV6_V6ONLY.
+ */
+#if defined(IPV6_V6ONLY) && !defined(__OpenBSD__)
+ if (res->ai_family == AF_INET6 && (domain == AF_UNSPEC || domain == AF_INET6)) {
+ if (domain == AF_UNSPEC)
+ opt = 0;
+ else
+ opt = 1;
+ if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY,
+ (char *) &opt, sizeof(opt)) < 0) {
+ saved_errno = errno;
+ close(s);
+ freeaddrinfo(res);
+ errno = saved_errno;
+ return -1;
+ }
+ }
+#endif /* IPV6_V6ONLY */
+
+ if (bind(s, (struct sockaddr *) res->ai_addr, res->ai_addrlen) < 0) {
+ saved_errno = errno;
+ close(s);
+ freeaddrinfo(res);
+ errno = saved_errno;
+ return -1;
+ }
+
+ freeaddrinfo(res);
+
+ if (proto == SOCK_STREAM) {
+ if (listen(s, INT_MAX) < 0) {
+ saved_errno = errno;
+ close(s);
+ errno = saved_errno;
+ return -1;
+ }
+ }
+
+ return s;
+}
+
+
+/*******************************************************************/
+/* reads 'count' bytes from a socket */
+/********************************************************************/
+
+int
+Nread(int fd, char *buf, size_t count, int prot)
+{
+ register ssize_t r;
+ register size_t nleft = count;
+ struct iperf_time ftimeout = { 0, 0 };
+
+ fd_set rfdset;
+ struct timeval timeout = { nread_read_timeout, 0 };
+
+ /*
+ * fd might not be ready for reading on entry. Check for this
+ * (with timeout) first.
+ *
+ * This check could go inside the while() loop below, except we're
+ * currently considering whether it might make sense to support a
+ * codepath that bypassese this check, for situations where we
+ * already know that fd has data on it (for example if we'd gotten
+ * to here as the result of a select() call.
+ */
+ {
+ FD_ZERO(&rfdset);
+ FD_SET(fd, &rfdset);
+ r = select(fd + 1, &rfdset, NULL, NULL, &timeout);
+ if (r < 0) {
+ return NET_HARDERROR;
+ }
+ if (r == 0) {
+ return 0;
+ }
+ }
+
+ while (nleft > 0) {
+ r = read(fd, buf, nleft);
+ if (r < 0) {
+ /* XXX EWOULDBLOCK can't happen without non-blocking sockets */
+ if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)
+ break;
+ else
+ return NET_HARDERROR;
+ } else if (r == 0)
+ break;
+
+ nleft -= r;
+ buf += r;
+
+ /*
+ * We need some more bytes but don't want to wait around
+ * forever for them. In the case of partial results, we need
+ * to be able to read some bytes every nread_timeout seconds.
+ */
+ if (nleft > 0) {
+ struct iperf_time now;
+
+ /*
+ * Also, we have an approximate upper limit for the total time
+ * that a Nread call is supposed to take. We trade off accuracy
+ * of this timeout for a hopefully lower performance impact.
+ */
+ iperf_time_now(&now);
+ if (ftimeout.secs == 0) {
+ ftimeout = now;
+ iperf_time_add_usecs(&ftimeout, nread_overall_timeout * 1000000L);
+ }
+ if (iperf_time_compare(&ftimeout, &now) < 0) {
+ break;
+ }
+
+ FD_ZERO(&rfdset);
+ FD_SET(fd, &rfdset);
+ r = select(fd + 1, &rfdset, NULL, NULL, &timeout);
+ if (r < 0) {
+ return NET_HARDERROR;
+ }
+ if (r == 0) {
+ break;
+ }
+ }
+ }
+ return count - nleft;
+}
+
+
+/*
+ * N W R I T E
+ */
+
+int
+Nwrite(int fd, const char *buf, size_t count, int prot)
+{
+ register ssize_t r;
+ register size_t nleft = count;
+
+ while (nleft > 0) {
+ r = write(fd, buf, nleft);
+ if (r < 0) {
+ switch (errno) {
+ case EINTR:
+ case EAGAIN:
+#if (EAGAIN != EWOULDBLOCK)
+ /* XXX EWOULDBLOCK can't happen without non-blocking sockets */
+ case EWOULDBLOCK:
+#endif
+ return count - nleft;
+
+ case ENOBUFS:
+ return NET_SOFTERROR;
+
+ default:
+ return NET_HARDERROR;
+ }
+ } else if (r == 0)
+ return NET_SOFTERROR;
+ nleft -= r;
+ buf += r;
+ }
+ return count;
+}
+
+
+int
+has_sendfile(void)
+{
+#if defined(HAVE_SENDFILE)
+ return 1;
+#else /* HAVE_SENDFILE */
+ return 0;
+#endif /* HAVE_SENDFILE */
+
+}
+
+
+/*
+ * N S E N D F I L E
+ */
+
+int
+Nsendfile(int fromfd, int tofd, const char *buf, size_t count)
+{
+#if defined(HAVE_SENDFILE)
+ off_t offset;
+#if defined(__FreeBSD__) || (defined(__APPLE__) && defined(__MACH__) && defined(MAC_OS_X_VERSION_10_6))
+ off_t sent;
+#endif
+ register size_t nleft;
+ register ssize_t r;
+
+ nleft = count;
+ while (nleft > 0) {
+ offset = count - nleft;
+#ifdef linux
+ r = sendfile(tofd, fromfd, &offset, nleft);
+ if (r > 0)
+ nleft -= r;
+#elif defined(__FreeBSD__)
+ r = sendfile(fromfd, tofd, offset, nleft, NULL, &sent, 0);
+ nleft -= sent;
+#elif defined(__APPLE__) && defined(__MACH__) && defined(MAC_OS_X_VERSION_10_6) /* OS X */
+ sent = nleft;
+ r = sendfile(fromfd, tofd, offset, &sent, NULL, 0);
+ nleft -= sent;
+#else
+ /* Shouldn't happen. */
+ r = -1;
+ errno = ENOSYS;
+#endif
+ if (r < 0) {
+ switch (errno) {
+ case EINTR:
+ case EAGAIN:
+#if (EAGAIN != EWOULDBLOCK)
+ /* XXX EWOULDBLOCK can't happen without non-blocking sockets */
+ case EWOULDBLOCK:
+#endif
+ if (count == nleft)
+ return NET_SOFTERROR;
+ return count - nleft;
+
+ case ENOBUFS:
+ case ENOMEM:
+ return NET_SOFTERROR;
+
+ default:
+ return NET_HARDERROR;
+ }
+ }
+#ifdef linux
+ else if (r == 0)
+ return NET_SOFTERROR;
+#endif
+ }
+ return count;
+#else /* HAVE_SENDFILE */
+ errno = ENOSYS; /* error if somehow get called without HAVE_SENDFILE */
+ return NET_HARDERROR;
+#endif /* HAVE_SENDFILE */
+}
+
+/*************************************************************************/
+
+int
+setnonblocking(int fd, int nonblocking)
+{
+ int flags, newflags;
+
+ flags = fcntl(fd, F_GETFL, 0);
+ if (flags < 0) {
+ perror("fcntl(F_GETFL)");
+ return -1;
+ }
+ if (nonblocking)
+ newflags = flags | (int) O_NONBLOCK;
+ else
+ newflags = flags & ~((int) O_NONBLOCK);
+ if (newflags != flags)
+ if (fcntl(fd, F_SETFL, newflags) < 0) {
+ perror("fcntl(F_SETFL)");
+ return -1;
+ }
+ return 0;
+}
+
+/****************************************************************************/
+
+int
+getsockdomain(int sock)
+{
+ struct sockaddr_storage sa;
+ socklen_t len = sizeof(sa);
+
+ if (getsockname(sock, (struct sockaddr *)&sa, &len) < 0) {
+ return -1;
+ }
+ return ((struct sockaddr *) &sa)->sa_family;
+}
diff --git a/src/net.h b/src/net.h
new file mode 100644
index 0000000..f0e1b4f
--- /dev/null
+++ b/src/net.h
@@ -0,0 +1,45 @@
+/*
+ * iperf, Copyright (c) 2014, 2017, 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.
+ *
+ * If you have questions about your rights to use or distribute this
+ * software, please contact Berkeley Lab's Technology Transfer
+ * Department at TTD@lbl.gov.
+ *
+ * NOTICE. This software is owned by the U.S. Department of Energy.
+ * As such, the U.S. Government has been granted for itself and others
+ * acting on its behalf a paid-up, nonexclusive, irrevocable,
+ * worldwide license in the Software to reproduce, prepare derivative
+ * works, and perform publicly and display publicly. Beginning five
+ * (5) years after the date permission to assert copyright is obtained
+ * from the U.S. Department of Energy, and subject to any subsequent
+ * five (5) year renewals, the U.S. Government is granted for itself
+ * and others acting on its behalf a paid-up, nonexclusive,
+ * irrevocable, worldwide license in the Software to reproduce,
+ * prepare derivative works, distribute copies to the public, perform
+ * publicly and display publicly, and to permit others to do so.
+ *
+ * This code is distributed under a BSD style license, see the LICENSE
+ * file for complete information.
+ */
+#ifndef __NET_H
+#define __NET_H
+
+int timeout_connect(int s, const struct sockaddr *name, socklen_t namelen, int timeout);
+int create_socket(int domain, int proto, const char *local, const char *bind_dev, int local_port, const char *server, int port, struct addrinfo **server_res_out);
+int netdial(int domain, int proto, const char *local, const char *bind_dev, int local_port, const char *server, int port, int timeout);
+int netannounce(int domain, int proto, const char *local, const char *bind_dev, int port);
+int Nread(int fd, char *buf, size_t count, int prot);
+int Nwrite(int fd, const char *buf, size_t count, int prot) /* __attribute__((hot)) */;
+int has_sendfile(void);
+int Nsendfile(int fromfd, int tofd, const char *buf, size_t count) /* __attribute__((hot)) */;
+int setnonblocking(int fd, int nonblocking);
+int getsockdomain(int sock);
+int parse_qos(const char *tos);
+
+#define NET_SOFTERROR -1
+#define NET_HARDERROR -2
+
+#endif /* __NET_H */
diff --git a/src/portable_endian.h b/src/portable_endian.h
new file mode 100644
index 0000000..c3c73ff
--- /dev/null
+++ b/src/portable_endian.h
@@ -0,0 +1,164 @@
+// "License": Public Domain
+// I, Mathias Panzenböck, place this file hereby into the public domain. Use it at your own risk for whatever you like.
+
+#ifndef PORTABLE_ENDIAN_H__
+#define PORTABLE_ENDIAN_H__
+
+#if (defined(_WIN16) || defined(_WIN32) || defined(_WIN64)) && !defined(__WINDOWS__)
+
+# define __WINDOWS__
+
+#endif
+
+#if defined(__CYGWIN__)
+
+# include <endian.h>
+
+#elif defined(HAVE_ENDIAN_H)
+# include <endian.h>
+
+#elif defined(HAVE_SYS_ENDIAN_H)
+# include <sys/endian.h>
+
+# if defined(__sgi)
+
+# include <netinet/in.h>
+# include <inttypes.h>
+
+# define be64toh(x) (x)
+# define htobe64(x) (x)
+
+# endif
+
+#elif defined(__APPLE__)
+
+# include <libkern/OSByteOrder.h>
+
+# define htobe16(x) OSSwapHostToBigInt16(x)
+# define htole16(x) OSSwapHostToLittleInt16(x)
+# define be16toh(x) OSSwapBigToHostInt16(x)
+# define le16toh(x) OSSwapLittleToHostInt16(x)
+
+# define htobe32(x) OSSwapHostToBigInt32(x)
+# define htole32(x) OSSwapHostToLittleInt32(x)
+# define be32toh(x) OSSwapBigToHostInt32(x)
+# define le32toh(x) OSSwapLittleToHostInt32(x)
+
+# define htobe64(x) OSSwapHostToBigInt64(x)
+# define htole64(x) OSSwapHostToLittleInt64(x)
+# define be64toh(x) OSSwapBigToHostInt64(x)
+# define le64toh(x) OSSwapLittleToHostInt64(x)
+
+# define __BYTE_ORDER BYTE_ORDER
+# define __BIG_ENDIAN BIG_ENDIAN
+# define __LITTLE_ENDIAN LITTLE_ENDIAN
+# define __PDP_ENDIAN PDP_ENDIAN
+
+#elif defined(__sun) && defined(__SVR4)
+
+# include <sys/types.h>
+# include <netinet/in.h>
+# include <inttypes.h>
+
+# if !defined (ntohll) || !defined(htonll)
+# ifdef _BIG_ENDIAN
+# define htonll(x) (x)
+# define ntohll(x) (x)
+# else
+# define htonll(x) ((((uint64_t)htonl(x)) << 32) + htonl((uint64_t)(x) >> 32))
+# define ntohll(x) ((((uint64_t)ntohl(x)) << 32) + ntohl((uint64_t)(x) >> 32))
+# endif
+# endif
+
+# define be64toh(x) ntohll(x)
+# define htobe64(x) htonll(x)
+
+#elif defined(__WINDOWS__)
+
+# include <winsock2.h>
+# include <sys/param.h>
+
+# if BYTE_ORDER == LITTLE_ENDIAN
+
+# define htobe16(x) htons(x)
+# define htole16(x) (x)
+# define be16toh(x) ntohs(x)
+# define le16toh(x) (x)
+
+# define htobe32(x) htonl(x)
+# define htole32(x) (x)
+# define be32toh(x) ntohl(x)
+# define le32toh(x) (x)
+
+# define htobe64(x) htonll(x)
+# define htole64(x) (x)
+# define be64toh(x) ntohll(x)
+# define le64toh(x) (x)
+
+# elif BYTE_ORDER == BIG_ENDIAN
+
+ /* that would be xbox 360 */
+# define htobe16(x) (x)
+# define htole16(x) __builtin_bswap16(x)
+# define be16toh(x) (x)
+# define le16toh(x) __builtin_bswap16(x)
+
+# define htobe32(x) (x)
+# define htole32(x) __builtin_bswap32(x)
+# define be32toh(x) (x)
+# define le32toh(x) __builtin_bswap32(x)
+
+# define htobe64(x) (x)
+# define htole64(x) __builtin_bswap64(x)
+# define be64toh(x) (x)
+# define le64toh(x) __builtin_bswap64(x)
+
+# else
+
+# error byte order not supported
+
+# endif
+
+# define __BYTE_ORDER BYTE_ORDER
+# define __BIG_ENDIAN BIG_ENDIAN
+# define __LITTLE_ENDIAN LITTLE_ENDIAN
+# define __PDP_ENDIAN PDP_ENDIAN
+
+#else
+
+// Unsupported platforms.
+// Intended to support CentOS 5 but hopefully not too far from
+// the truth because we use the homebrew htonll, et al. implementations
+// that were originally the sole implementation of this functionality
+// in iperf 3.0.
+# warning platform not supported
+# include <endian.h>
+#if BYTE_ORDER == BIG_ENDIAN
+#define HTONLL(n) (n)
+#define NTOHLL(n) (n)
+#else
+#define HTONLL(n) ((((unsigned long long)(n) & 0xFF) << 56) | \
+ (((unsigned long long)(n) & 0xFF00) << 40) | \
+ (((unsigned long long)(n) & 0xFF0000) << 24) | \
+ (((unsigned long long)(n) & 0xFF000000) << 8) | \
+ (((unsigned long long)(n) & 0xFF00000000) >> 8) | \
+ (((unsigned long long)(n) & 0xFF0000000000) >> 24) | \
+ (((unsigned long long)(n) & 0xFF000000000000) >> 40) | \
+ (((unsigned long long)(n) & 0xFF00000000000000) >> 56))
+
+#define NTOHLL(n) ((((unsigned long long)(n) & 0xFF) << 56) | \
+ (((unsigned long long)(n) & 0xFF00) << 40) | \
+ (((unsigned long long)(n) & 0xFF0000) << 24) | \
+ (((unsigned long long)(n) & 0xFF000000) << 8) | \
+ (((unsigned long long)(n) & 0xFF00000000) >> 8) | \
+ (((unsigned long long)(n) & 0xFF0000000000) >> 24) | \
+ (((unsigned long long)(n) & 0xFF000000000000) >> 40) | \
+ (((unsigned long long)(n) & 0xFF00000000000000) >> 56))
+#endif
+
+#define htobe64(n) HTONLL(n)
+#define be64toh(n) NTOHLL(n)
+
+#endif
+
+#endif
diff --git a/src/private.pem b/src/private.pem
new file mode 100644
index 0000000..8b1c5b6
--- /dev/null
+++ b/src/private.pem
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEAwVbvPf/eDIZKmEVth9+VPgSx1RkXOAPCJ5tl51bcYoy9P10N
+noutsTK/64VclIuyUDUdAw81Vu5tYRcBeB8Jllp02xL8kPo0IROsT3wmMYbPziUG
+/I2988sAP9mL8QbtKydnADHMikfadfyPkfxW2naFtquWT/vKKVkhC2LHJyTpmAVj
+pp6R9LDDu/YY0/kb5DvYPpe62xgNWjNVIhABu3R+StAAL25SaLXuaGSVpDe6Sphn
+TqVf+LmKCkZmSMSfQoozXomNyFpLU8ODoV3QyHYi6QSWWFtU6gu0uso/9pfRFjbN
+GV9lxokKNz/cZGyu0SFddMiTGrt24NROi0RHwwIDAQABAoIBACVG+cHef5WyntdV
+K5UzCrd2eEM6HzvxnZG9aJx+JufpcuOwsVuMWuT7f/2NLRiHBs5oLzvTxtkIB5bc
+tK/QbCzNLBLBSmk5lKt7+5EnwsVx1MdOZFZ1jdZfoaCt9Ul3qGrVogprj6Bp0jlF
+hPkEyko85/McilLJnWTzhmeHmBZ3tOJ9LWHgVdXGXZx/pBuxZ+UB/xgZdvdZK86F
+xndWqD8lvfpoSsVwzCORdXvQWs78CtT2KXYvt8S6mrLMoIoHLO/1rwwKKoZfFuja
+NouN36PGaSo4/9O1d11/s9zwdJH3ShozY0Fao8I3XhdH0uvTirmAEPh7U1Lo71pP
+ksUvbUECgYEA/7p1Bg/XsSK72KgJ695B2hi8g9+wv0eKCpGwkKJQZKE338it8zIn
++JMaeDjObLhb4B2QP/3iL0mdU2mJzm0X8hYCKqBdyICb9wixznLLAej+uw2j60Ef
+tlGenCqAkVL3pRS5txztNhFsXx3JwxCSj4zwmVm5oyxkpAeuwvZ1cPkCgYEAwYuC
+6nPlRf6GEf/MuHCziaCCPrDm1u472uspxHn41crw6BaMl5xcmr0Z8O7heOtlVmMl
+Mn3gfIbd/NGyjs8ejmWcO5mZHtESGM/zAaVyu7bn3ij37OEndbEpZsCpRtxvtaji
+MTMwhdV2xteJ0+KCtg66ziSyJv3krQTEW8DZKZsCgYB8BcnLbtOErPu9T4HASsJV
+K7oBmvL1UZS5G38uJgonQ6j9dy4lzCVmgLFNrP8v6xljz/Ktlkuj82fBlGWpH2+F
+kPbsBWp2WylI3YaeQT4DZyRjQ3JEHglrOppZ0qMX180S2sJW9Eh2+Gw+lQvM9rSd
+uhTVypYldNo6Ux+GnlDGwQKBgQC/6W4uvCyjcvXN8y8z08yysw1yzEaY6DFBqd0I
+jUlH9Ktb9sABtXG9nbSTSssX85HQTw8bOeXWlISZo/TB1m4eFHMORgemnviq0cfL
+4hoaOAtCJq1vnPJbqQe8c11mfj3mi0d+MZvzmO7ly+NGzlt92q0wqwJb13VgelGa
+CWdL8QKBgQD1ABK8WJuMowihrQsLaUxNO/1hsN8CP/rjoi9D5NdSFt5zzcC3D/RA
+m42ScOaAFIh44Js5aAROvbDqIlelhwRlmutP/lYyQRZDZm7X6u/bKuqK4H/sM6ZB
+SICe2eWFvbKexI7QnreRltOWIFrNoZy9FdELM8DS+kePAlVt7c83Uw==
+-----END RSA PRIVATE KEY-----
diff --git a/src/public.pem b/src/public.pem
new file mode 100644
index 0000000..71f4f13
--- /dev/null
+++ b/src/public.pem
@@ -0,0 +1,9 @@
+-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwVbvPf/eDIZKmEVth9+V
+PgSx1RkXOAPCJ5tl51bcYoy9P10NnoutsTK/64VclIuyUDUdAw81Vu5tYRcBeB8J
+llp02xL8kPo0IROsT3wmMYbPziUG/I2988sAP9mL8QbtKydnADHMikfadfyPkfxW
+2naFtquWT/vKKVkhC2LHJyTpmAVjpp6R9LDDu/YY0/kb5DvYPpe62xgNWjNVIhAB
+u3R+StAAL25SaLXuaGSVpDe6SphnTqVf+LmKCkZmSMSfQoozXomNyFpLU8ODoV3Q
+yHYi6QSWWFtU6gu0uso/9pfRFjbNGV9lxokKNz/cZGyu0SFddMiTGrt24NROi0RH
+wwIDAQAB
+-----END PUBLIC KEY-----
diff --git a/src/queue.h b/src/queue.h
new file mode 100644
index 0000000..f741a4c
--- /dev/null
+++ b/src/queue.h
@@ -0,0 +1,527 @@
+/* $OpenBSD: queue.h,v 1.32 2007/04/30 18:42:34 pedro Exp $ */
+/* $NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $ */
+
+/*
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)queue.h 8.5 (Berkeley) 8/20/94
+ */
+
+#ifndef _SYS_QUEUE_H_
+#define _SYS_QUEUE_H_
+
+/*
+ * This file defines five types of data structures: singly-linked lists,
+ * lists, simple queues, tail queues, and circular queues.
+ *
+ *
+ * A singly-linked list is headed by a single forward pointer. The elements
+ * are singly linked for minimum space and pointer manipulation overhead at
+ * the expense of O(n) removal for arbitrary elements. New elements can be
+ * added to the list after an existing element or at the head of the list.
+ * Elements being removed from the head of the list should use the explicit
+ * macro for this purpose for optimum efficiency. A singly-linked list may
+ * only be traversed in the forward direction. Singly-linked lists are ideal
+ * for applications with large datasets and few or no removals or for
+ * implementing a LIFO queue.
+ *
+ * A list is headed by a single forward pointer (or an array of forward
+ * pointers for a hash table header). The elements are doubly linked
+ * so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before
+ * or after an existing element or at the head of the list. A list
+ * may only be traversed in the forward direction.
+ *
+ * A simple queue is headed by a pair of pointers, one the head of the
+ * list and the other to the tail of the list. The elements are singly
+ * linked to save space, so elements can only be removed from the
+ * head of the list. New elements can be added to the list before or after
+ * an existing element, at the head of the list, or at the end of the
+ * list. A simple queue may only be traversed in the forward direction.
+ *
+ * A tail queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or
+ * after an existing element, at the head of the list, or at the end of
+ * the list. A tail queue may be traversed in either direction.
+ *
+ * A circle queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or after
+ * an existing element, at the head of the list, or at the end of the list.
+ * A circle queue may be traversed in either direction, but has a more
+ * complex end of list detection.
+ *
+ * For details on the use of these macros, see the queue(3) manual page.
+ */
+
+#if defined(QUEUE_MACRO_DEBUG) || (defined(_KERNEL) && defined(DIAGNOSTIC))
+#define _Q_INVALIDATE(a) (a) = ((void *)-1)
+#else
+#define _Q_INVALIDATE(a)
+#endif
+
+/*
+ * Singly-linked List definitions.
+ */
+#define SLIST_HEAD(name, type) \
+struct name { \
+ struct type *slh_first; /* first element */ \
+}
+
+#define SLIST_HEAD_INITIALIZER(head) \
+ { NULL }
+
+#define SLIST_ENTRY(type) \
+struct { \
+ struct type *sle_next; /* next element */ \
+}
+
+/*
+ * Singly-linked List access methods.
+ */
+#define SLIST_FIRST(head) ((head)->slh_first)
+#define SLIST_END(head) NULL
+#define SLIST_EMPTY(head) (SLIST_FIRST(head) == SLIST_END(head))
+#define SLIST_NEXT(elm, field) ((elm)->field.sle_next)
+
+#define SLIST_FOREACH(var, head, field) \
+ for((var) = SLIST_FIRST(head); \
+ (var) != SLIST_END(head); \
+ (var) = SLIST_NEXT(var, field))
+
+#define SLIST_FOREACH_PREVPTR(var, varp, head, field) \
+ for ((varp) = &SLIST_FIRST((head)); \
+ ((var) = *(varp)) != SLIST_END(head); \
+ (varp) = &SLIST_NEXT((var), field))
+
+/*
+ * Singly-linked List functions.
+ */
+#define SLIST_INIT(head) { \
+ SLIST_FIRST(head) = SLIST_END(head); \
+}
+
+#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \
+ (elm)->field.sle_next = (slistelm)->field.sle_next; \
+ (slistelm)->field.sle_next = (elm); \
+} while (0)
+
+#define SLIST_INSERT_HEAD(head, elm, field) do { \
+ (elm)->field.sle_next = (head)->slh_first; \
+ (head)->slh_first = (elm); \
+} while (0)
+
+#define SLIST_REMOVE_NEXT(head, elm, field) do { \
+ (elm)->field.sle_next = (elm)->field.sle_next->field.sle_next; \
+} while (0)
+
+#define SLIST_REMOVE_HEAD(head, field) do { \
+ (head)->slh_first = (head)->slh_first->field.sle_next; \
+} while (0)
+
+#define SLIST_REMOVE(head, elm, type, field) do { \
+ if ((head)->slh_first == (elm)) { \
+ SLIST_REMOVE_HEAD((head), field); \
+ } else { \
+ struct type *curelm = (head)->slh_first; \
+ \
+ while (curelm->field.sle_next != (elm)) \
+ curelm = curelm->field.sle_next; \
+ curelm->field.sle_next = \
+ curelm->field.sle_next->field.sle_next; \
+ _Q_INVALIDATE((elm)->field.sle_next); \
+ } \
+} while (0)
+
+/*
+ * List definitions.
+ */
+#define LIST_HEAD(name, type) \
+struct name { \
+ struct type *lh_first; /* first element */ \
+}
+
+#define LIST_HEAD_INITIALIZER(head) \
+ { NULL }
+
+#define LIST_ENTRY(type) \
+struct { \
+ struct type *le_next; /* next element */ \
+ struct type **le_prev; /* address of previous next element */ \
+}
+
+/*
+ * List access methods
+ */
+#define LIST_FIRST(head) ((head)->lh_first)
+#define LIST_END(head) NULL
+#define LIST_EMPTY(head) (LIST_FIRST(head) == LIST_END(head))
+#define LIST_NEXT(elm, field) ((elm)->field.le_next)
+
+#define LIST_FOREACH(var, head, field) \
+ for((var) = LIST_FIRST(head); \
+ (var)!= LIST_END(head); \
+ (var) = LIST_NEXT(var, field))
+
+/*
+ * List functions.
+ */
+#define LIST_INIT(head) do { \
+ LIST_FIRST(head) = LIST_END(head); \
+} while (0)
+
+#define LIST_INSERT_AFTER(listelm, elm, field) do { \
+ if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \
+ (listelm)->field.le_next->field.le_prev = \
+ &(elm)->field.le_next; \
+ (listelm)->field.le_next = (elm); \
+ (elm)->field.le_prev = &(listelm)->field.le_next; \
+} while (0)
+
+#define LIST_INSERT_BEFORE(listelm, elm, field) do { \
+ (elm)->field.le_prev = (listelm)->field.le_prev; \
+ (elm)->field.le_next = (listelm); \
+ *(listelm)->field.le_prev = (elm); \
+ (listelm)->field.le_prev = &(elm)->field.le_next; \
+} while (0)
+
+#define LIST_INSERT_HEAD(head, elm, field) do { \
+ if (((elm)->field.le_next = (head)->lh_first) != NULL) \
+ (head)->lh_first->field.le_prev = &(elm)->field.le_next;\
+ (head)->lh_first = (elm); \
+ (elm)->field.le_prev = &(head)->lh_first; \
+} while (0)
+
+#define LIST_REMOVE(elm, field) do { \
+ if ((elm)->field.le_next != NULL) \
+ (elm)->field.le_next->field.le_prev = \
+ (elm)->field.le_prev; \
+ *(elm)->field.le_prev = (elm)->field.le_next; \
+ _Q_INVALIDATE((elm)->field.le_prev); \
+ _Q_INVALIDATE((elm)->field.le_next); \
+} while (0)
+
+#define LIST_REPLACE(elm, elm2, field) do { \
+ if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \
+ (elm2)->field.le_next->field.le_prev = \
+ &(elm2)->field.le_next; \
+ (elm2)->field.le_prev = (elm)->field.le_prev; \
+ *(elm2)->field.le_prev = (elm2); \
+ _Q_INVALIDATE((elm)->field.le_prev); \
+ _Q_INVALIDATE((elm)->field.le_next); \
+} while (0)
+
+/*
+ * Simple queue definitions.
+ */
+#define SIMPLEQ_HEAD(name, type) \
+struct name { \
+ struct type *sqh_first; /* first element */ \
+ struct type **sqh_last; /* addr of last next element */ \
+}
+
+#define SIMPLEQ_HEAD_INITIALIZER(head) \
+ { NULL, &(head).sqh_first }
+
+#define SIMPLEQ_ENTRY(type) \
+struct { \
+ struct type *sqe_next; /* next element */ \
+}
+
+/*
+ * Simple queue access methods.
+ */
+#define SIMPLEQ_FIRST(head) ((head)->sqh_first)
+#define SIMPLEQ_END(head) NULL
+#define SIMPLEQ_EMPTY(head) (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head))
+#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next)
+
+#define SIMPLEQ_FOREACH(var, head, field) \
+ for((var) = SIMPLEQ_FIRST(head); \
+ (var) != SIMPLEQ_END(head); \
+ (var) = SIMPLEQ_NEXT(var, field))
+
+/*
+ * Simple queue functions.
+ */
+#define SIMPLEQ_INIT(head) do { \
+ (head)->sqh_first = NULL; \
+ (head)->sqh_last = &(head)->sqh_first; \
+} while (0)
+
+#define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \
+ if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \
+ (head)->sqh_last = &(elm)->field.sqe_next; \
+ (head)->sqh_first = (elm); \
+} while (0)
+
+#define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \
+ (elm)->field.sqe_next = NULL; \
+ *(head)->sqh_last = (elm); \
+ (head)->sqh_last = &(elm)->field.sqe_next; \
+} while (0)
+
+#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
+ if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\
+ (head)->sqh_last = &(elm)->field.sqe_next; \
+ (listelm)->field.sqe_next = (elm); \
+} while (0)
+
+#define SIMPLEQ_REMOVE_HEAD(head, field) do { \
+ if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \
+ (head)->sqh_last = &(head)->sqh_first; \
+} while (0)
+
+/*
+ * Tail queue definitions.
+ */
+#define TAILQ_HEAD(name, type) \
+struct name { \
+ struct type *tqh_first; /* first element */ \
+ struct type **tqh_last; /* addr of last next element */ \
+}
+
+#define TAILQ_HEAD_INITIALIZER(head) \
+ { NULL, &(head).tqh_first }
+
+#define TAILQ_ENTRY(type) \
+struct { \
+ struct type *tqe_next; /* next element */ \
+ struct type **tqe_prev; /* address of previous next element */ \
+}
+
+/*
+ * tail queue access methods
+ */
+#define TAILQ_FIRST(head) ((head)->tqh_first)
+#define TAILQ_END(head) NULL
+#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
+#define TAILQ_LAST(head, headname) \
+ (*(((struct headname *)((head)->tqh_last))->tqh_last))
+/* XXX */
+#define TAILQ_PREV(elm, headname, field) \
+ (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
+#define TAILQ_EMPTY(head) \
+ (TAILQ_FIRST(head) == TAILQ_END(head))
+
+#define TAILQ_FOREACH(var, head, field) \
+ for((var) = TAILQ_FIRST(head); \
+ (var) != TAILQ_END(head); \
+ (var) = TAILQ_NEXT(var, field))
+
+#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \
+ for((var) = TAILQ_LAST(head, headname); \
+ (var) != TAILQ_END(head); \
+ (var) = TAILQ_PREV(var, headname, field))
+
+/*
+ * Tail queue functions.
+ */
+#define TAILQ_INIT(head) do { \
+ (head)->tqh_first = NULL; \
+ (head)->tqh_last = &(head)->tqh_first; \
+} while (0)
+
+#define TAILQ_INSERT_HEAD(head, elm, field) do { \
+ if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \
+ (head)->tqh_first->field.tqe_prev = \
+ &(elm)->field.tqe_next; \
+ else \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+ (head)->tqh_first = (elm); \
+ (elm)->field.tqe_prev = &(head)->tqh_first; \
+} while (0)
+
+#define TAILQ_INSERT_TAIL(head, elm, field) do { \
+ (elm)->field.tqe_next = NULL; \
+ (elm)->field.tqe_prev = (head)->tqh_last; \
+ *(head)->tqh_last = (elm); \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+} while (0)
+
+#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
+ if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\
+ (elm)->field.tqe_next->field.tqe_prev = \
+ &(elm)->field.tqe_next; \
+ else \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+ (listelm)->field.tqe_next = (elm); \
+ (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \
+} while (0)
+
+#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
+ (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
+ (elm)->field.tqe_next = (listelm); \
+ *(listelm)->field.tqe_prev = (elm); \
+ (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \
+} while (0)
+
+#define TAILQ_REMOVE(head, elm, field) do { \
+ if (((elm)->field.tqe_next) != NULL) \
+ (elm)->field.tqe_next->field.tqe_prev = \
+ (elm)->field.tqe_prev; \
+ else \
+ (head)->tqh_last = (elm)->field.tqe_prev; \
+ *(elm)->field.tqe_prev = (elm)->field.tqe_next; \
+ _Q_INVALIDATE((elm)->field.tqe_prev); \
+ _Q_INVALIDATE((elm)->field.tqe_next); \
+} while (0)
+
+#define TAILQ_REPLACE(head, elm, elm2, field) do { \
+ if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL) \
+ (elm2)->field.tqe_next->field.tqe_prev = \
+ &(elm2)->field.tqe_next; \
+ else \
+ (head)->tqh_last = &(elm2)->field.tqe_next; \
+ (elm2)->field.tqe_prev = (elm)->field.tqe_prev; \
+ *(elm2)->field.tqe_prev = (elm2); \
+ _Q_INVALIDATE((elm)->field.tqe_prev); \
+ _Q_INVALIDATE((elm)->field.tqe_next); \
+} while (0)
+
+/*
+ * Circular queue definitions.
+ */
+#define CIRCLEQ_HEAD(name, type) \
+struct name { \
+ struct type *cqh_first; /* first element */ \
+ struct type *cqh_last; /* last element */ \
+}
+
+#define CIRCLEQ_HEAD_INITIALIZER(head) \
+ { CIRCLEQ_END(&head), CIRCLEQ_END(&head) }
+
+#define CIRCLEQ_ENTRY(type) \
+struct { \
+ struct type *cqe_next; /* next element */ \
+ struct type *cqe_prev; /* previous element */ \
+}
+
+/*
+ * Circular queue access methods
+ */
+#define CIRCLEQ_FIRST(head) ((head)->cqh_first)
+#define CIRCLEQ_LAST(head) ((head)->cqh_last)
+#define CIRCLEQ_END(head) ((void *)(head))
+#define CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next)
+#define CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev)
+#define CIRCLEQ_EMPTY(head) \
+ (CIRCLEQ_FIRST(head) == CIRCLEQ_END(head))
+
+#define CIRCLEQ_FOREACH(var, head, field) \
+ for((var) = CIRCLEQ_FIRST(head); \
+ (var) != CIRCLEQ_END(head); \
+ (var) = CIRCLEQ_NEXT(var, field))
+
+#define CIRCLEQ_FOREACH_REVERSE(var, head, field) \
+ for((var) = CIRCLEQ_LAST(head); \
+ (var) != CIRCLEQ_END(head); \
+ (var) = CIRCLEQ_PREV(var, field))
+
+/*
+ * Circular queue functions.
+ */
+#define CIRCLEQ_INIT(head) do { \
+ (head)->cqh_first = CIRCLEQ_END(head); \
+ (head)->cqh_last = CIRCLEQ_END(head); \
+} while (0)
+
+#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
+ (elm)->field.cqe_next = (listelm)->field.cqe_next; \
+ (elm)->field.cqe_prev = (listelm); \
+ if ((listelm)->field.cqe_next == CIRCLEQ_END(head)) \
+ (head)->cqh_last = (elm); \
+ else \
+ (listelm)->field.cqe_next->field.cqe_prev = (elm); \
+ (listelm)->field.cqe_next = (elm); \
+} while (0)
+
+#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \
+ (elm)->field.cqe_next = (listelm); \
+ (elm)->field.cqe_prev = (listelm)->field.cqe_prev; \
+ if ((listelm)->field.cqe_prev == CIRCLEQ_END(head)) \
+ (head)->cqh_first = (elm); \
+ else \
+ (listelm)->field.cqe_prev->field.cqe_next = (elm); \
+ (listelm)->field.cqe_prev = (elm); \
+} while (0)
+
+#define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \
+ (elm)->field.cqe_next = (head)->cqh_first; \
+ (elm)->field.cqe_prev = CIRCLEQ_END(head); \
+ if ((head)->cqh_last == CIRCLEQ_END(head)) \
+ (head)->cqh_last = (elm); \
+ else \
+ (head)->cqh_first->field.cqe_prev = (elm); \
+ (head)->cqh_first = (elm); \
+} while (0)
+
+#define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \
+ (elm)->field.cqe_next = CIRCLEQ_END(head); \
+ (elm)->field.cqe_prev = (head)->cqh_last; \
+ if ((head)->cqh_first == CIRCLEQ_END(head)) \
+ (head)->cqh_first = (elm); \
+ else \
+ (head)->cqh_last->field.cqe_next = (elm); \
+ (head)->cqh_last = (elm); \
+} while (0)
+
+#define CIRCLEQ_REMOVE(head, elm, field) do { \
+ if ((elm)->field.cqe_next == CIRCLEQ_END(head)) \
+ (head)->cqh_last = (elm)->field.cqe_prev; \
+ else \
+ (elm)->field.cqe_next->field.cqe_prev = \
+ (elm)->field.cqe_prev; \
+ if ((elm)->field.cqe_prev == CIRCLEQ_END(head)) \
+ (head)->cqh_first = (elm)->field.cqe_next; \
+ else \
+ (elm)->field.cqe_prev->field.cqe_next = \
+ (elm)->field.cqe_next; \
+ _Q_INVALIDATE((elm)->field.cqe_prev); \
+ _Q_INVALIDATE((elm)->field.cqe_next); \
+} while (0)
+
+#define CIRCLEQ_REPLACE(head, elm, elm2, field) do { \
+ if (((elm2)->field.cqe_next = (elm)->field.cqe_next) == \
+ CIRCLEQ_END(head)) \
+ (head).cqh_last = (elm2); \
+ else \
+ (elm2)->field.cqe_next->field.cqe_prev = (elm2); \
+ if (((elm2)->field.cqe_prev = (elm)->field.cqe_prev) == \
+ CIRCLEQ_END(head)) \
+ (head).cqh_first = (elm2); \
+ else \
+ (elm2)->field.cqe_prev->field.cqe_next = (elm2); \
+ _Q_INVALIDATE((elm)->field.cqe_prev); \
+ _Q_INVALIDATE((elm)->field.cqe_next); \
+} while (0)
+
+#endif /* !_SYS_QUEUE_H_ */
diff --git a/src/t_api.c b/src/t_api.c
new file mode 100644
index 0000000..d822f55
--- /dev/null
+++ b/src/t_api.c
@@ -0,0 +1,92 @@
+/*
+ * iperf, Copyright (c) 2017-2020, The Regents of the University of
+ * California, through Lawrence Berkeley National Laboratory (subject
+ * to receipt of any required approvals from the U.S. Dept. of
+ * Energy). All rights reserved.
+ *
+ * If you have questions about your rights to use or distribute this
+ * software, please contact Berkeley Lab's Technology Transfer
+ * Department at TTD@lbl.gov.
+ *
+ * NOTICE. This software is owned by the U.S. Department of Energy.
+ * As such, the U.S. Government has been granted for itself and others
+ * acting on its behalf a paid-up, nonexclusive, irrevocable,
+ * worldwide license in the Software to reproduce, prepare derivative
+ * works, and perform publicly and display publicly. Beginning five
+ * (5) years after the date permission to assert copyright is obtained
+ * from the U.S. Department of Energy, and subject to any subsequent
+ * five (5) year renewals, the U.S. Government is granted for itself
+ * and others acting on its behalf a paid-up, nonexclusive,
+ * irrevocable, worldwide license in the Software to reproduce,
+ * prepare derivative works, distribute copies to the public, perform
+ * publicly and display publicly, and to permit others to do so.
+ *
+ * This code is distributed under a BSD style license, see the LICENSE
+ * file for complete information.
+ */
+
+
+#include <assert.h>
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif
+#include <stdio.h>
+#include <string.h>
+
+#include "iperf.h"
+#include "iperf_api.h"
+
+#include "version.h"
+
+#include "units.h"
+
+int test_iperf_set_test_bind_port(struct iperf_test *test)
+{
+ int port;
+ port = iperf_get_test_bind_port(test);
+ iperf_set_test_bind_port(test, 5202);
+ port = iperf_get_test_bind_port(test);
+ assert(port == 5202);
+ return 0;
+}
+
+int test_iperf_set_mss(struct iperf_test *test)
+{
+ int mss = iperf_get_test_mss(test);
+ iperf_set_test_mss(test, 535);
+ mss = iperf_get_test_mss(test);
+ assert(mss == 535);
+ return 0;
+}
+
+int
+main(int argc, char **argv)
+{
+ const char *ver;
+ struct iperf_test *test;
+ int sint, gint;
+
+ ver = iperf_get_iperf_version();
+ assert(strcmp(ver, IPERF_VERSION) == 0);
+
+ test = iperf_new_test();
+ assert(test != NULL);
+
+ iperf_defaults(test);
+
+ sint = 10;
+ iperf_set_test_connect_timeout(test, sint);
+ gint = iperf_get_test_connect_timeout(test);
+ assert(sint == gint);
+
+ int ret;
+ ret = test_iperf_set_test_bind_port(test);
+
+ ret += test_iperf_set_mss(test);
+
+ if (ret < 0)
+ {
+ return -1;
+ }
+ return 0;
+}
diff --git a/src/t_auth.c b/src/t_auth.c
new file mode 100644
index 0000000..22c78ae
--- /dev/null
+++ b/src/t_auth.c
@@ -0,0 +1,125 @@
+/*
+ * iperf, Copyright (c) 2020, The Regents of the University of
+ * California, through Lawrence Berkeley National Laboratory (subject
+ * to receipt of any required approvals from the U.S. Dept. of
+ * Energy). All rights reserved.
+ *
+ * If you have questions about your rights to use or distribute this
+ * software, please contact Berkeley Lab's Technology Transfer
+ * Department at TTD@lbl.gov.
+ *
+ * NOTICE. This software is owned by the U.S. Department of Energy.
+ * As such, the U.S. Government has been granted for itself and others
+ * acting on its behalf a paid-up, nonexclusive, irrevocable,
+ * worldwide license in the Software to reproduce, prepare derivative
+ * works, and perform publicly and display publicly. Beginning five
+ * (5) years after the date permission to assert copyright is obtained
+ * from the U.S. Department of Energy, and subject to any subsequent
+ * five (5) year renewals, the U.S. Government is granted for itself
+ * and others acting on its behalf a paid-up, nonexclusive,
+ * irrevocable, worldwide license in the Software to reproduce,
+ * prepare derivative works, distribute copies to the public, perform
+ * publicly and display publicly, and to permit others to do so.
+ *
+ * This code is distributed under a BSD style license, see the LICENSE
+ * file for complete information.
+ */
+#include "iperf_config.h"
+
+#include <assert.h>
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif
+#include <stdio.h>
+#include <string.h>
+
+#include "iperf.h"
+#include "iperf_api.h"
+#if defined(HAVE_SSL)
+#include "iperf_auth.h"
+#endif /* HAVE_SSL */
+
+#include "version.h"
+
+#include "units.h"
+
+#if defined(HAVE_SSL)
+int test_authtoken(const char *authUser, const char *authPassword, EVP_PKEY *pubkey, EVP_PKEY *privkey);
+
+int
+main(int argc, char **argv)
+{
+ /* sha256 */
+ void sha256(const char *string, char outputBuffer[65]);
+ const char sha256String[] = "This is a SHA256 test.";
+ const char sha256Digest[] = "4816482f8b4149f687a1a33d61a0de6b611364ec0fb7adffa59ff2af672f7232"; /* echo -n "This is a SHA256 test." | shasum -a256 */
+ char sha256Output[65];
+
+ sha256(sha256String, sha256Output);
+ assert(strcmp(sha256Output, sha256Digest) == 0);
+
+ /* Base64{Encode,Decode} */
+ int Base64Encode(const unsigned char* buffer, const size_t length, char** b64text);
+ int Base64Decode(const char* b64message, unsigned char** buffer, size_t* length);
+ const char base64String[] = "This is a Base64 test.";
+ char *base64Text;
+ char *base64Decode;
+ size_t base64DecodeLength;
+ const char base64EncodeCheck[] = "VGhpcyBpcyBhIEJhc2U2NCB0ZXN0Lg=="; /* echo -n "This is a Base64 test." | b64encode -r - */
+
+ assert(Base64Encode((unsigned char *) base64String, strlen(base64String), &base64Text) == 0);
+ assert(strcmp(base64Text, base64EncodeCheck) == 0);
+ assert(Base64Decode(base64Text, (unsigned char **) &base64Decode, &base64DecodeLength) == 0);
+ assert(strcmp(base64String, base64Decode) == 0);
+
+ /* public/private key tests */
+ const char *pubkeyfile = "public.pem";
+ const char *privkeyfile = "private.pem";
+
+ /* built-in tests */
+ assert(test_load_pubkey_from_file(pubkeyfile) == 0);
+ assert(test_load_private_key_from_file(privkeyfile) == 0);
+
+ /* load public key pair for use in further tests */
+ EVP_PKEY *pubkey, *privkey;
+ pubkey = load_pubkey_from_file(pubkeyfile);
+ assert(pubkey);
+ privkey = load_privkey_from_file(privkeyfile);
+ assert(privkey);
+
+ /* authentication token tests */
+ assert(test_authtoken("kilroy", "fubar", pubkey, privkey) == 0);
+
+ /* This should fail because the data is way too long for the RSA key */
+ /* assert(test_authtoken("kilroykilroykilroykilroykilroykilroykilroykilroykilroykilroykilroykilroykilroykilroykilroykilroykilroykilroykilroykilroykilroykilroykilroykilroykilroykilroy", "fubarfubarfubarfubarfubarfubarfubarfubarfubarfubarfubarfubarfubarfubarfubarfubarfubarfubarfubarfubarfubarfubarfubarfubarfubarfubarfubarfubarfubarfubarfubar", pubkey, privkey) < 0); */
+
+ return 0;
+}
+
+int
+test_authtoken(const char *authUser, const char *authPassword, EVP_PKEY *pubkey, EVP_PKEY *privkey) {
+ char *authToken;
+ char *decodeUser;
+ char *decodePassword;
+ time_t decodeTime;
+
+ assert(encode_auth_setting(authUser, authPassword, pubkey, &authToken) == 0);
+ assert(decode_auth_setting(0, authToken, privkey, &decodeUser, &decodePassword, &decodeTime) == 0);
+
+ assert(strcmp(decodeUser, authUser) == 0);
+ assert(strcmp(decodePassword, authPassword) == 0);
+
+ time_t now = time(NULL);
+
+ assert(now - decodeTime >= 0); /* time has to go forwards */
+ assert(now - decodeTime <= 1); /* shouldn't take more than a second to run */
+
+ return 0;
+}
+#else
+int
+main(int argc, char **argv)
+{
+ return 0;
+}
+#endif /* HAVE_SSL */
diff --git a/src/t_timer.c b/src/t_timer.c
new file mode 100644
index 0000000..8eec7d8
--- /dev/null
+++ b/src/t_timer.c
@@ -0,0 +1,83 @@
+/*
+ * iperf, Copyright (c) 2014, 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.
+ *
+ * If you have questions about your rights to use or distribute this
+ * software, please contact Berkeley Lab's Technology Transfer
+ * Department at TTD@lbl.gov.
+ *
+ * NOTICE. This software is owned by the U.S. Department of Energy.
+ * As such, the U.S. Government has been granted for itself and others
+ * acting on its behalf a paid-up, nonexclusive, irrevocable,
+ * worldwide license in the Software to reproduce, prepare derivative
+ * works, and perform publicly and display publicly. Beginning five
+ * (5) years after the date permission to assert copyright is obtained
+ * from the U.S. Department of Energy, and subject to any subsequent
+ * five (5) year renewals, the U.S. Government is granted for itself
+ * and others acting on its behalf a paid-up, nonexclusive,
+ * irrevocable, worldwide license in the Software to reproduce,
+ * prepare derivative works, distribute copies to the public, perform
+ * publicly and display publicly, and to permit others to do so.
+ *
+ * This code is distributed under a BSD style license, see the LICENSE
+ * file for complete information.
+ */
+#include "iperf_config.h"
+
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/time.h>
+
+#include "timer.h"
+#include "iperf_time.h"
+
+
+static int flag;
+
+
+static void
+timer_proc( TimerClientData client_data, struct iperf_time* nowP )
+{
+ flag = 1;
+}
+
+
+int
+main(int argc, char **argv)
+{
+ Timer *tp;
+
+ flag = 0;
+ tp = tmr_create(NULL, timer_proc, JunkClientData, 3000000, 0);
+ if (!tp)
+ {
+ printf("failed to create timer\n");
+ exit(-1);
+ }
+
+ sleep(2);
+
+ tmr_run(NULL);
+ if (flag)
+ {
+ printf("timer should not have expired\n");
+ exit(-1);
+ }
+ sleep(1);
+
+ tmr_run(NULL);
+ if (!flag)
+ {
+ printf("timer should have expired\n");
+ exit(-2);
+ }
+
+ tmr_destroy();
+ exit(0);
+}
diff --git a/src/t_units.c b/src/t_units.c
new file mode 100644
index 0000000..73f21a9
--- /dev/null
+++ b/src/t_units.c
@@ -0,0 +1,110 @@
+/*
+ * iperf, Copyright (c) 2014, 2017, 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.
+ *
+ * If you have questions about your rights to use or distribute this
+ * software, please contact Berkeley Lab's Technology Transfer
+ * Department at TTD@lbl.gov.
+ *
+ * NOTICE. This software is owned by the U.S. Department of Energy.
+ * As such, the U.S. Government has been granted for itself and others
+ * acting on its behalf a paid-up, nonexclusive, irrevocable,
+ * worldwide license in the Software to reproduce, prepare derivative
+ * works, and perform publicly and display publicly. Beginning five
+ * (5) years after the date permission to assert copyright is obtained
+ * from the U.S. Department of Energy, and subject to any subsequent
+ * five (5) year renewals, the U.S. Government is granted for itself
+ * and others acting on its behalf a paid-up, nonexclusive,
+ * irrevocable, worldwide license in the Software to reproduce,
+ * prepare derivative works, distribute copies to the public, perform
+ * publicly and display publicly, and to permit others to do so.
+ *
+ * This code is distributed under a BSD style license, see the LICENSE
+ * file for complete information.
+ */
+#include <assert.h>
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif
+#include <stdio.h>
+#include <string.h>
+
+#include "iperf.h"
+#include "units.h"
+
+int
+main(int argc, char **argv)
+{
+ iperf_size_t llu;
+ double d;
+ char s[11];
+
+ assert(1024.0 * 0.5 == unit_atof("0.5K"));
+ assert(1024.0 == unit_atof("1K"));
+ assert(1024.0 * 1024.0 == unit_atof("1M"));
+ assert(4.0 * 1024.0 * 1024.0 * 1024.0 == unit_atof("4G"));
+ assert(3.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 == unit_atof("3T"));
+
+ assert(1024.0 * 0.5 == unit_atof("0.5k"));
+ assert(1024.0 == unit_atof("1k"));
+ assert(1024.0 * 1024.0 == unit_atof("1m"));
+ assert(4.0 * 1024.0 * 1024.0 * 1024.0 == unit_atof("4g"));
+ assert(3.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 == unit_atof("3t"));
+
+ assert(1024 * 0.5 == unit_atoi("0.5K"));
+ assert(1024 == unit_atoi("1K"));
+ assert(1024 * 1024 == unit_atoi("1M"));
+ d = 4.0 * 1024 * 1024 * 1024;
+ llu = (iperf_size_t) d;
+ assert(llu == unit_atoi("4G"));
+ d = 3.0 * 1024 * 1024 * 1024 * 1024;
+ llu = (iperf_size_t) d;
+ assert(llu == unit_atoi("3T"));
+
+ assert(1024 * 0.5 == unit_atoi("0.5k"));
+ assert(1024 == unit_atoi("1k"));
+ assert(1024 * 1024 == unit_atoi("1m"));
+ d = 4.0 * 1024 * 1024 * 1024;
+ llu = (iperf_size_t) d;
+ assert(llu == unit_atoi("4g"));
+ d = 3.0 * 1024 * 1024 * 1024 * 1024;
+ llu = (iperf_size_t) d;
+ assert(llu == unit_atoi("3t"));
+
+ unit_snprintf(s, 11, 1024.0, 'A');
+ assert(strncmp(s, "1.00 KByte", 11) == 0);
+
+ unit_snprintf(s, 11, 1024.0 * 1024.0, 'A');
+ assert(strncmp(s, "1.00 MByte", 11) == 0);
+
+ unit_snprintf(s, 11, 1000.0, 'k');
+ assert(strncmp(s, "8.00 Kbit", 11) == 0);
+
+ unit_snprintf(s, 11, 1000.0 * 1000.0, 'a');
+ assert(strncmp(s, "8.00 Mbit", 11) == 0);
+
+ d = 4.0 * 1024 * 1024 * 1024;
+ unit_snprintf(s, 11, d, 'A');
+ assert(strncmp(s, "4.00 GByte", 11) == 0);
+
+ unit_snprintf(s, 11, d, 'a');
+ assert(strncmp(s, "34.4 Gbit", 11) == 0);
+
+ d = 4.0 * 1024 * 1024 * 1024 * 1024;
+ unit_snprintf(s, 11, d, 'A');
+ assert(strncmp(s, "4.00 TByte", 11) == 0);
+
+ unit_snprintf(s, 11, d, 'a');
+ assert(strncmp(s, "35.2 Tbit", 11) == 0);
+
+ d = 4.0 * 1024 * 1024 * 1024 * 1024 * 1024;
+ unit_snprintf(s, 11, d, 'A');
+ assert(strncmp(s, "4096 TByte", 11) == 0);
+
+ unit_snprintf(s, 11, d, 'a');
+ assert(strncmp(s, "36029 Tbit", 11) == 0);
+
+ return 0;
+}
diff --git a/src/t_uuid.c b/src/t_uuid.c
new file mode 100644
index 0000000..49064e1
--- /dev/null
+++ b/src/t_uuid.c
@@ -0,0 +1,46 @@
+/*
+ * iperf, Copyright (c) 2014, 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.
+ *
+ * If you have questions about your rights to use or distribute this
+ * software, please contact Berkeley Lab's Technology Transfer
+ * Department at TTD@lbl.gov.
+ *
+ * NOTICE. This software is owned by the U.S. Department of Energy.
+ * As such, the U.S. Government has been granted for itself and others
+ * acting on its behalf a paid-up, nonexclusive, irrevocable,
+ * worldwide license in the Software to reproduce, prepare derivative
+ * works, and perform publicly and display publicly. Beginning five
+ * (5) years after the date permission to assert copyright is obtained
+ * from the U.S. Department of Energy, and subject to any subsequent
+ * five (5) year renewals, the U.S. Government is granted for itself
+ * and others acting on its behalf a paid-up, nonexclusive,
+ * irrevocable, worldwide license in the Software to reproduce,
+ * prepare derivative works, distribute copies to the public, perform
+ * publicly and display publicly, and to permit others to do so.
+ *
+ * This code is distributed under a BSD style license, see the LICENSE
+ * file for complete information.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+
+#include "iperf_util.h"
+
+int
+main(int argc, char **argv)
+{
+ char cookie[37];
+ make_cookie(cookie);
+ printf("cookie: '%s'\n", cookie);
+ if (strlen(cookie) != 36)
+ {
+ printf("Not 36 characters long!\n");
+ exit(-1);
+ }
+ exit(0);
+}
diff --git a/src/tcp_info.c b/src/tcp_info.c
new file mode 100644
index 0000000..160063c
--- /dev/null
+++ b/src/tcp_info.c
@@ -0,0 +1,234 @@
+/*
+ * iperf, Copyright (c) 2014, 2017, 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.
+ *
+ * If you have questions about your rights to use or distribute this
+ * software, please contact Berkeley Lab's Technology Transfer
+ * Department at TTD@lbl.gov.
+ *
+ * NOTICE. This software is owned by the U.S. Department of Energy.
+ * As such, the U.S. Government has been granted for itself and others
+ * acting on its behalf a paid-up, nonexclusive, irrevocable,
+ * worldwide license in the Software to reproduce, prepare derivative
+ * works, and perform publicly and display publicly. Beginning five
+ * (5) years after the date permission to assert copyright is obtained
+ * from the U.S. Department of Energy, and subject to any subsequent
+ * five (5) year renewals, the U.S. Government is granted for itself
+ * and others acting on its behalf a paid-up, nonexclusive,
+ * irrevocable, worldwide license in the Software to reproduce,
+ * prepare derivative works, distribute copies to the public, perform
+ * publicly and display publicly, and to permit others to do so.
+ *
+ * This code is distributed under a BSD style license, see the LICENSE
+ * file for complete information.
+ */
+
+/*
+ * routines related to collection TCP_INFO using getsockopt()
+ *
+ * Brian Tierney, ESnet (bltierney@es.net)
+ *
+ * Note that this is only really useful on Linux.
+ * XXX: only standard on linux versions 2.4 and later
+ #
+ * FreeBSD has a limited implementation that only includes the following:
+ * tcpi_snd_ssthresh, tcpi_snd_cwnd, tcpi_rcv_space, tcpi_rtt
+ * Based on information on http://wiki.freebsd.org/8.0TODO, I dont think this will be
+ * fixed before v8.1 at the earliest.
+ *
+ * OSX has no support.
+ *
+ * I think MS Windows does support TCP_INFO, but iperf3 does not currently support Windows.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <string.h>
+#include <netinet/in.h>
+#include <errno.h>
+
+#include "iperf.h"
+#include "iperf_api.h"
+#include "iperf_locale.h"
+
+/*************************************************************/
+int
+has_tcpinfo(void)
+{
+#if (defined(linux) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)) \
+ && defined(TCP_INFO)
+ return 1;
+#else
+ return 0;
+#endif
+}
+
+/*************************************************************/
+int
+has_tcpinfo_retransmits(void)
+{
+#if defined(linux) && defined(TCP_MD5SIG)
+ /* TCP_MD5SIG doesn't actually have anything to do with TCP
+ ** retransmits, it just showed up in the same rev of the header
+ ** file. If it's present then struct tcp_info has the
+ ** tcpi_total_retrans field that we need; if not, not.
+ */
+ return 1;
+#else
+#if defined(__FreeBSD__) && __FreeBSD_version >= 600000
+ return 1; /* Should work now */
+#elif (defined(__NetBSD__) || defined(__OpenBSD__)) && defined(TCP_INFO)
+ return 1;
+#else
+ return 0;
+#endif
+#endif
+}
+
+/*************************************************************/
+void
+save_tcpinfo(struct iperf_stream *sp, struct iperf_interval_results *irp)
+{
+#if (defined(linux) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)) && \
+ defined(TCP_INFO)
+ socklen_t tcp_info_length = sizeof(struct tcp_info);
+
+ if (getsockopt(sp->socket, IPPROTO_TCP, TCP_INFO, (void *)&irp->tcpInfo, &tcp_info_length) < 0)
+ iperf_err(sp->test, "getsockopt - %s", strerror(errno));
+
+ if (sp->test->debug) {
+ printf("tcpi_snd_cwnd %u tcpi_snd_mss %u tcpi_rtt %u\n",
+ irp->tcpInfo.tcpi_snd_cwnd, irp->tcpInfo.tcpi_snd_mss,
+ irp->tcpInfo.tcpi_rtt);
+ }
+
+#endif
+}
+
+/*************************************************************/
+long
+get_total_retransmits(struct iperf_interval_results *irp)
+{
+#if defined(linux) && defined(TCP_MD5SIG)
+ return irp->tcpInfo.tcpi_total_retrans;
+#elif defined(__FreeBSD__) && __FreeBSD_version >= 600000
+ return irp->tcpInfo.tcpi_snd_rexmitpack;
+#elif (defined(__NetBSD__) || defined(__OpenBSD__)) && defined(TCP_INFO)
+ return irp->tcpInfo.tcpi_snd_rexmitpack;
+#else
+ return -1;
+#endif
+}
+
+/*************************************************************/
+/*
+ * Return snd_cwnd in octets.
+ */
+long
+get_snd_cwnd(struct iperf_interval_results *irp)
+{
+#if defined(linux) && defined(TCP_MD5SIG)
+ return (long)irp->tcpInfo.tcpi_snd_cwnd * irp->tcpInfo.tcpi_snd_mss;
+#elif defined(__FreeBSD__) && __FreeBSD_version >= 600000
+ return irp->tcpInfo.tcpi_snd_cwnd;
+#elif defined(__NetBSD__) && defined(TCP_INFO)
+ return (long)irp->tcpInfo.tcpi_snd_cwnd * irp->tcpInfo.tcpi_snd_mss;
+#elif defined(__OpenBSD__) && defined(TCP_INFO)
+ return irp->tcpInfo.tcpi_snd_cwnd;
+#else
+ return -1;
+#endif
+}
+
+/*************************************************************/
+/*
+ * Return snd_wnd in octets.
+ */
+long
+get_snd_wnd(struct iperf_interval_results *irp)
+{
+#if !defined(HAVE_TCP_INFO_SND_WND)
+ return -1;
+#elif defined(linux) && defined(TCP_MD5SIG)
+ return irp->tcpInfo.tcpi_snd_wnd;
+#elif defined(__FreeBSD__) && __FreeBSD_version >= 600000
+ return irp->tcpInfo.tcpi_snd_wnd;
+#elif defined(__NetBSD__) && defined(TCP_INFO)
+ return (long)irp->tcpInfo.tcpi_snd_wnd * irp->tcpInfo.tcpi_snd_mss;
+#elif defined(__OpenBSD__) && defined(TCP_INFO)
+ return irp->tcpInfo.tcpi_snd_wnd;
+#else
+ return -1;
+#endif
+}
+
+/*************************************************************/
+/*
+ * Return rtt in usec.
+ */
+long
+get_rtt(struct iperf_interval_results *irp)
+{
+#if defined(linux) && defined(TCP_MD5SIG)
+ return irp->tcpInfo.tcpi_rtt;
+#elif defined(__FreeBSD__) && __FreeBSD_version >= 600000
+ return irp->tcpInfo.tcpi_rtt;
+#elif (defined(__NetBSD__) || defined(__OpenBSD__)) && defined(TCP_INFO)
+ return irp->tcpInfo.tcpi_rtt;
+#else
+ return -1;
+#endif
+}
+
+/*************************************************************/
+/*
+ * Return rttvar in usec.
+ */
+long
+get_rttvar(struct iperf_interval_results *irp)
+{
+#if defined(linux) && defined(TCP_MD5SIG)
+ return irp->tcpInfo.tcpi_rttvar;
+#elif defined(__FreeBSD__) && __FreeBSD_version >= 600000
+ return irp->tcpInfo.tcpi_rttvar;
+#elif (defined(__NetBSD__) || defined(__OpenBSD__)) && defined(TCP_INFO)
+ return irp->tcpInfo.tcpi_rttvar;
+#else
+ return -1;
+#endif
+}
+
+/*************************************************************/
+/*
+ * Return PMTU in bytes.
+ */
+long
+get_pmtu(struct iperf_interval_results *irp)
+{
+#if defined(linux) && defined(TCP_MD5SIG)
+ return irp->tcpInfo.tcpi_pmtu;
+#else
+ return -1;
+#endif
+}
+
+/*************************************************************/
+void
+build_tcpinfo_message(struct iperf_interval_results *r, char *message)
+{
+#if defined(linux) && defined(TCP_INFO)
+ sprintf(message, report_tcpInfo, r->tcpInfo.tcpi_snd_cwnd, r->tcpInfo.tcpi_snd_ssthresh,
+ r->tcpInfo.tcpi_rcv_ssthresh, r->tcpInfo.tcpi_unacked, r->tcpInfo.tcpi_sacked,
+ r->tcpInfo.tcpi_lost, r->tcpInfo.tcpi_retrans, r->tcpInfo.tcpi_fackets,
+ r->tcpInfo.tcpi_rtt, r->tcpInfo.tcpi_reordering);
+#endif
+#if (defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)) && defined(TCP_INFO)
+ sprintf(message, report_tcpInfo, r->tcpInfo.tcpi_snd_cwnd,
+ r->tcpInfo.tcpi_rcv_space, r->tcpInfo.tcpi_snd_ssthresh, r->tcpInfo.tcpi_rtt);
+#endif
+}
diff --git a/src/timer.c b/src/timer.c
new file mode 100644
index 0000000..644eeab
--- /dev/null
+++ b/src/timer.c
@@ -0,0 +1,245 @@
+/*
+ * iperf, Copyright (c) 2014, 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.
+ *
+ * If you have questions about your rights to use or distribute this
+ * software, please contact Berkeley Lab's Technology Transfer
+ * Department at TTD@lbl.gov.
+ *
+ * NOTICE. This software is owned by the U.S. Department of Energy.
+ * As such, the U.S. Government has been granted for itself and others
+ * acting on its behalf a paid-up, nonexclusive, irrevocable,
+ * worldwide license in the Software to reproduce, prepare derivative
+ * works, and perform publicly and display publicly. Beginning five
+ * (5) years after the date permission to assert copyright is obtained
+ * from the U.S. Department of Energy, and subject to any subsequent
+ * five (5) year renewals, the U.S. Government is granted for itself
+ * and others acting on its behalf a paid-up, nonexclusive,
+ * irrevocable, worldwide license in the Software to reproduce,
+ * prepare derivative works, distribute copies to the public, perform
+ * publicly and display publicly, and to permit others to do so.
+ *
+ * This code is distributed under a BSD style license, see the LICENSE
+ * file for complete information.
+ *
+ * Based on timers.c by Jef Poskanzer. Used with permission.
+ */
+
+#include <sys/types.h>
+#include <stdlib.h>
+
+#include "timer.h"
+#include "iperf_time.h"
+
+static Timer* timers = NULL;
+static Timer* free_timers = NULL;
+
+TimerClientData JunkClientData;
+
+
+
+/* This is an efficiency tweak. All the routines that need to know the
+** current time get passed a pointer to a struct iperf_time. If it's non-NULL
+** it gets used, otherwise we do our own iperf_time_now() to fill it in.
+** This lets the caller avoid extraneous iperf_time_now()s when efficiency
+** is needed, and not bother with the extra code when efficiency doesn't
+** matter too much.
+*/
+static void
+getnow( struct iperf_time* nowP, struct iperf_time* nowP2 )
+{
+ if ( nowP != NULL )
+ *nowP2 = *nowP;
+ else
+ iperf_time_now(nowP2);
+}
+
+
+static void
+list_add( Timer* t )
+{
+ Timer* t2;
+ Timer* t2prev;
+
+ if ( timers == NULL ) {
+ /* The list is empty. */
+ timers = t;
+ t->prev = t->next = NULL;
+ } else {
+ if (iperf_time_compare(&t->time, &timers->time) < 0) {
+ /* The new timer goes at the head of the list. */
+ t->prev = NULL;
+ t->next = timers;
+ timers->prev = t;
+ timers = t;
+ } else {
+ /* Walk the list to find the insertion point. */
+ for ( t2prev = timers, t2 = timers->next; t2 != NULL;
+ t2prev = t2, t2 = t2->next ) {
+ if (iperf_time_compare(&t->time, &t2->time) < 0) {
+ /* Found it. */
+ t2prev->next = t;
+ t->prev = t2prev;
+ t->next = t2;
+ t2->prev = t;
+ return;
+ }
+ }
+ /* Oops, got to the end of the list. Add to tail. */
+ t2prev->next = t;
+ t->prev = t2prev;
+ t->next = NULL;
+ }
+ }
+}
+
+
+static void
+list_remove( Timer* t )
+{
+ if ( t->prev == NULL )
+ timers = t->next;
+ else
+ t->prev->next = t->next;
+ if ( t->next != NULL )
+ t->next->prev = t->prev;
+}
+
+
+static void
+list_resort( Timer* t )
+{
+ /* Remove the timer from the list. */
+ list_remove( t );
+ /* And add it back in, sorted correctly. */
+ list_add( t );
+}
+
+
+Timer*
+tmr_create(
+ struct iperf_time* nowP, TimerProc* timer_proc, TimerClientData client_data,
+ int64_t usecs, int periodic )
+{
+ struct iperf_time now;
+ Timer* t;
+
+ getnow( nowP, &now );
+
+ if ( free_timers != NULL ) {
+ t = free_timers;
+ free_timers = t->next;
+ } else {
+ t = (Timer*) malloc( sizeof(Timer) );
+ if ( t == NULL )
+ return NULL;
+ }
+
+ t->timer_proc = timer_proc;
+ t->client_data = client_data;
+ t->usecs = usecs;
+ t->periodic = periodic;
+ t->time = now;
+ iperf_time_add_usecs(&t->time, usecs);
+ /* Add the new timer to the active list. */
+ list_add( t );
+
+ return t;
+}
+
+
+struct timeval*
+tmr_timeout( struct iperf_time* nowP )
+{
+ struct iperf_time now, diff;
+ int64_t usecs;
+ int past;
+ static struct timeval timeout;
+
+ getnow( nowP, &now );
+ /* Since the list is sorted, we only need to look at the first timer. */
+ if ( timers == NULL )
+ return NULL;
+ past = iperf_time_diff(&timers->time, &now, &diff);
+ if (past)
+ usecs = 0;
+ else
+ usecs = iperf_time_in_usecs(&diff);
+ timeout.tv_sec = usecs / 1000000LL;
+ timeout.tv_usec = usecs % 1000000LL;
+ return &timeout;
+}
+
+
+void
+tmr_run( struct iperf_time* nowP )
+{
+ struct iperf_time now;
+ Timer* t;
+ Timer* next;
+
+ getnow( nowP, &now );
+ for ( t = timers; t != NULL; t = next ) {
+ next = t->next;
+ /* Since the list is sorted, as soon as we find a timer
+ ** that isn't ready yet, we are done.
+ */
+ if (iperf_time_compare(&t->time, &now) > 0)
+ break;
+ (t->timer_proc)( t->client_data, &now );
+ if ( t->periodic ) {
+ /* Reschedule. */
+ iperf_time_add_usecs(&t->time, t->usecs);
+ list_resort( t );
+ } else
+ tmr_cancel( t );
+ }
+}
+
+
+void
+tmr_reset( struct iperf_time* nowP, Timer* t )
+{
+ struct iperf_time now;
+
+ getnow( nowP, &now );
+ t->time = now;
+ iperf_time_add_usecs( &t->time, t->usecs );
+ list_resort( t );
+}
+
+
+void
+tmr_cancel( Timer* t )
+{
+ /* Remove it from the active list. */
+ list_remove( t );
+ /* And put it on the free list. */
+ t->next = free_timers;
+ free_timers = t;
+ t->prev = NULL;
+}
+
+
+void
+tmr_cleanup( void )
+{
+ Timer* t;
+
+ while ( free_timers != NULL ) {
+ t = free_timers;
+ free_timers = t->next;
+ free( (void*) t );
+ }
+}
+
+
+void
+tmr_destroy( void )
+{
+ while ( timers != NULL )
+ tmr_cancel( timers );
+ tmr_cleanup();
+}
diff --git a/src/timer.h b/src/timer.h
new file mode 100644
index 0000000..58c3db8
--- /dev/null
+++ b/src/timer.h
@@ -0,0 +1,100 @@
+/*
+ * iperf, Copyright (c) 2014, 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.
+ *
+ * If you have questions about your rights to use or distribute this
+ * software, please contact Berkeley Lab's Technology Transfer
+ * Department at TTD@lbl.gov.
+ *
+ * NOTICE. This software is owned by the U.S. Department of Energy.
+ * As such, the U.S. Government has been granted for itself and others
+ * acting on its behalf a paid-up, nonexclusive, irrevocable,
+ * worldwide license in the Software to reproduce, prepare derivative
+ * works, and perform publicly and display publicly. Beginning five
+ * (5) years after the date permission to assert copyright is obtained
+ * from the U.S. Department of Energy, and subject to any subsequent
+ * five (5) year renewals, the U.S. Government is granted for itself
+ * and others acting on its behalf a paid-up, nonexclusive,
+ * irrevocable, worldwide license in the Software to reproduce,
+ * prepare derivative works, distribute copies to the public, perform
+ * publicly and display publicly, and to permit others to do so.
+ *
+ * This code is distributed under a BSD style license, see the LICENSE
+ * file for complete information.
+ *
+ * Based on timers.h by Jef Poskanzer. Used with permission.
+ */
+
+#ifndef __TIMER_H
+#define __TIMER_H
+
+#include <time.h>
+#include <sys/time.h>
+
+#include "iperf_time.h"
+
+/* TimerClientData is an opaque value that tags along with a timer. The
+** client can use it for whatever, and it gets passed to the callback when
+** the timer triggers.
+*/
+typedef union
+{
+ void* p;
+ int i;
+ long l;
+} TimerClientData;
+
+extern TimerClientData JunkClientData; /* for use when you don't care */
+
+/* The TimerProc gets called when the timer expires. It gets passed
+** the TimerClientData associated with the timer, and a iperf_time in case
+** it wants to schedule another timer.
+*/
+typedef void TimerProc( TimerClientData client_data, struct iperf_time* nowP );
+
+/* The Timer struct. */
+typedef struct TimerStruct
+{
+ TimerProc* timer_proc;
+ TimerClientData client_data;
+ int64_t usecs;
+ int periodic;
+ struct iperf_time time;
+ struct TimerStruct* prev;
+ struct TimerStruct* next;
+ int hash;
+} Timer;
+
+/* Set up a timer, either periodic or one-shot. Returns (Timer*) 0 on errors. */
+extern Timer* tmr_create(
+ struct iperf_time* nowP, TimerProc* timer_proc, TimerClientData client_data,
+ int64_t usecs, int periodic );
+
+/* Returns a timeout indicating how long until the next timer triggers. You
+** can just put the call to this routine right in your select(). Returns
+** (struct timeval*) 0 if no timers are pending.
+*/
+extern struct timeval* tmr_timeout( struct iperf_time* nowP ) /* __attribute__((hot)) */;
+
+/* Run the list of timers. Your main program needs to call this every so often,
+** or as indicated by tmr_timeout().
+*/
+extern void tmr_run( struct iperf_time* nowP ) /* __attribute__((hot)) */;
+
+/* Reset the clock on a timer, to current time plus the original timeout. */
+extern void tmr_reset( struct iperf_time* nowP, Timer* timer );
+
+/* Deschedule a timer. Note that non-periodic timers are automatically
+** descheduled when they run, so you don't have to call this on them.
+*/
+extern void tmr_cancel( Timer* timer );
+
+/* Clean up the timers package, freeing any unused storage. */
+extern void tmr_cleanup( void );
+
+/* Cancel all timers and free storage, usually in preparation for exiting. */
+extern void tmr_destroy( void );
+
+#endif /* __TIMER_H */
diff --git a/src/units.c b/src/units.c
new file mode 100644
index 0000000..7376a0b
--- /dev/null
+++ b/src/units.c
@@ -0,0 +1,355 @@
+/*---------------------------------------------------------------
+ * Copyright (c) 1999,2000,2001,2002,2003
+ * The Board of Trustees of the University of Illinois
+ * All Rights Reserved.
+ *---------------------------------------------------------------
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software (Iperf) and associated
+ * documentation files (the "Software"), to deal in the Software
+ * without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute,
+ * sublicense, and/or sell copies of the Software, and to permit
+ * persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ *
+ * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and
+ * the following disclaimers.
+ *
+ *
+ * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimers in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ *
+ * Neither the names of the University of Illinois, NCSA,
+ * nor the names of its contributors may be used to endorse
+ * or promote products derived from this Software without
+ * specific prior written permission.
+ *
+ * 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
+ * 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
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ * ________________________________________________________________
+ * National Laboratory for Applied Network Research
+ * National Center for Supercomputing Applications
+ * University of Illinois at Urbana-Champaign
+ * http://www.ncsa.uiuc.edu
+ * ________________________________________________________________
+ *
+ * stdio.c
+ * by Mark Gates <mgates@nlanr.net>
+ * and Ajay Tirumalla <tirumala@ncsa.uiuc.edu>
+ * -------------------------------------------------------------------
+ * input and output numbers, converting with kilo, mega, giga, tera
+ * ------------------------------------------------------------------- */
+
+#include <stdio.h>
+#include <assert.h>
+#include <ctype.h>
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/time.h>
+
+
+#include "iperf.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+ const double KILO_UNIT = 1024.0;
+ const double MEGA_UNIT = 1024.0 * 1024.0;
+ const double GIGA_UNIT = 1024.0 * 1024.0 * 1024.0;
+ const double TERA_UNIT = 1024.0 * 1024.0 * 1024.0 * 1024.0;
+
+ const double KILO_RATE_UNIT = 1000.0;
+ const double MEGA_RATE_UNIT = 1000.0 * 1000.0;
+ const double GIGA_RATE_UNIT = 1000.0 * 1000.0 * 1000.0;
+ const double TERA_RATE_UNIT = 1000.0 * 1000.0 * 1000.0 * 1000.0;
+
+/* -------------------------------------------------------------------
+ * unit_atof
+ *
+ * Given a string of form #x where # is a number and x is a format
+ * character listed below, this returns the interpreted integer.
+ * Gg, Mm, Kk are giga, mega, kilo respectively
+ * ------------------------------------------------------------------- */
+
+ double unit_atof(const char *s)
+ {
+ double n;
+ char suffix = '\0';
+
+ assert(s != NULL);
+
+ /* scan the number and any suffices */
+ sscanf(s, "%lf%c", &n, &suffix);
+
+ /* convert according to [Tt Gg Mm Kk] */
+ switch (suffix)
+ {
+ case 't': case 'T':
+ n *= TERA_UNIT;
+ break;
+ case 'g': case 'G':
+ n *= GIGA_UNIT;
+ break;
+ case 'm': case 'M':
+ n *= MEGA_UNIT;
+ break;
+ case 'k': case 'K':
+ n *= KILO_UNIT;
+ break;
+ default:
+ break;
+ }
+ return n;
+ } /* end unit_atof */
+
+
+/* -------------------------------------------------------------------
+ * unit_atof_rate
+ *
+ * Similar to unit_atof, but uses 10-based rather than 2-based
+ * suffixes.
+ * ------------------------------------------------------------------- */
+
+ double unit_atof_rate(const char *s)
+ {
+ double n;
+ char suffix = '\0';
+
+ assert(s != NULL);
+
+ /* scan the number and any suffices */
+ sscanf(s, "%lf%c", &n, &suffix);
+
+ /* convert according to [Tt Gg Mm Kk] */
+ switch (suffix)
+ {
+ case 't': case 'T':
+ n *= TERA_RATE_UNIT;
+ break;
+ case 'g': case 'G':
+ n *= GIGA_RATE_UNIT;
+ break;
+ case 'm': case 'M':
+ n *= MEGA_RATE_UNIT;
+ break;
+ case 'k': case 'K':
+ n *= KILO_RATE_UNIT;
+ break;
+ default:
+ break;
+ }
+ return n;
+ } /* end unit_atof_rate */
+
+
+
+/* -------------------------------------------------------------------
+ * unit_atoi
+ *
+ * Given a string of form #x where # is a number and x is a format
+ * character listed below, this returns the interpreted integer.
+ * Tt, Gg, Mm, Kk are tera, giga, mega, kilo respectively
+ * ------------------------------------------------------------------- */
+
+ iperf_size_t unit_atoi(const char *s)
+ {
+ double n;
+ char suffix = '\0';
+
+ assert(s != NULL);
+
+ /* scan the number and any suffices */
+ sscanf(s, "%lf%c", &n, &suffix);
+
+ /* convert according to [Tt Gg Mm Kk] */
+ switch (suffix)
+ {
+ case 't': case 'T':
+ n *= TERA_UNIT;
+ break;
+ case 'g': case 'G':
+ n *= GIGA_UNIT;
+ break;
+ case 'm': case 'M':
+ n *= MEGA_UNIT;
+ break;
+ case 'k': case 'K':
+ n *= KILO_UNIT;
+ break;
+ default:
+ break;
+ }
+ return (iperf_size_t) n;
+ } /* end unit_atof */
+
+/* -------------------------------------------------------------------
+ * constants for byte_printf
+ * ------------------------------------------------------------------- */
+
+/* used as indices into conversion_bytes[], label_byte[], and label_bit[] */
+ enum
+ {
+ UNIT_CONV,
+ KILO_CONV,
+ MEGA_CONV,
+ GIGA_CONV,
+ TERA_CONV
+ };
+
+/* factor to multiply the number by */
+ const double conversion_bytes[] =
+ {
+ 1.0, /* unit */
+ 1.0 / 1024, /* kilo */
+ 1.0 / 1024 / 1024, /* mega */
+ 1.0 / 1024 / 1024 / 1024, /* giga */
+ 1.0 / 1024 / 1024 / 1024 / 1024 /* tera */
+ };
+
+/* factor to multiply the number by for bits*/
+ const double conversion_bits[] =
+ {
+ 1.0, /* unit */
+ 1.0 / 1000, /* kilo */
+ 1.0 / 1000 / 1000, /* mega */
+ 1.0 / 1000 / 1000 / 1000, /* giga */
+ 1.0 / 1000 / 1000 / 1000 / 1000 /* tera */
+ };
+
+
+/* labels for Byte formats [KMGT] */
+ const char *label_byte[] =
+ {
+ "Byte",
+ "KByte",
+ "MByte",
+ "GByte",
+ "TByte"
+ };
+
+/* labels for bit formats [kmgt] */
+ const char *label_bit[] =
+ {
+ "bit",
+ "Kbit",
+ "Mbit",
+ "Gbit",
+ "Tbit"
+ };
+
+/* -------------------------------------------------------------------
+ * unit_snprintf
+ *
+ * Given a number in bytes and a format, converts the number and
+ * prints it out with a bits or bytes label.
+ * B, K, M, G, A for Byte, Kbyte, Mbyte, Gbyte, adaptive byte
+ * b, k, m, g, a for bit, Kbit, Mbit, Gbit, adaptive bit
+ * adaptive picks the "best" one based on the number.
+ * s should be at least 11 chars long
+ * (4 digits + space + 5 chars max + null)
+ * ------------------------------------------------------------------- */
+
+ void unit_snprintf(char *s, int inLen,
+ double inNum, char inFormat)
+ {
+ int conv;
+ const char *suffix;
+ const char *format;
+
+ /* convert to bits for [bkmga] */
+ if (!isupper((int) inFormat))
+ {
+ inNum *= 8;
+ }
+ switch (toupper((u_char)inFormat))
+ {
+ case 'B':
+ conv = UNIT_CONV;
+ break;
+ case 'K':
+ conv = KILO_CONV;
+ break;
+ case 'M':
+ conv = MEGA_CONV;
+ break;
+ case 'G':
+ conv = GIGA_CONV;
+ break;
+ case 'T':
+ conv = TERA_CONV;
+ break;
+
+ default:
+ case 'A':
+ {
+ double tmpNum = inNum;
+ conv = UNIT_CONV;
+
+ if (isupper((int) inFormat))
+ {
+ while (tmpNum >= 1024.0 && conv < TERA_CONV)
+ {
+ tmpNum /= 1024.0;
+ conv++;
+ }
+ } else
+ {
+ while (tmpNum >= 1000.0 && conv < TERA_CONV)
+ {
+ tmpNum /= 1000.0;
+ conv++;
+ }
+ }
+ break;
+ }
+ }
+
+ if (!isupper((int) inFormat))
+ {
+ inNum *= conversion_bits[conv];
+ suffix = label_bit[conv];
+ } else
+ {
+ inNum *= conversion_bytes[conv];
+ suffix = label_byte[conv];
+ }
+
+ /* print such that we always fit in 4 places */
+ if (inNum < 9.995)
+ { /* 9.995 would be rounded to 10.0 */
+ format = "%4.2f %s";/* #.## */
+ } else if (inNum < 99.95)
+ { /* 99.95 would be rounded to 100 */
+ format = "%4.1f %s";/* ##.# */
+ } else if (inNum < 999.5)
+ { /* 999.5 would be rounded to 1000 */
+ format = "%4.0f %s";/* ### */
+ } else
+ { /* 1000-1024 fits in 4 places If not using
+ * Adaptive sizes then this code will not
+ * control spaces */
+ format = "%4.0f %s";/* #### */
+ }
+ snprintf(s, inLen, format, inNum, suffix);
+ } /* end unit_snprintf */
+
+#ifdef __cplusplus
+} /* end extern "C" */
+
+#endif
diff --git a/src/units.h b/src/units.h
new file mode 100644
index 0000000..6ab9216
--- /dev/null
+++ b/src/units.h
@@ -0,0 +1,34 @@
+/*
+ * iperf, Copyright (c) 2014, 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.
+ *
+ * If you have questions about your rights to use or distribute this
+ * software, please contact Berkeley Lab's Technology Transfer
+ * Department at TTD@lbl.gov.
+ *
+ * NOTICE. This software is owned by the U.S. Department of Energy.
+ * As such, the U.S. Government has been granted for itself and others
+ * acting on its behalf a paid-up, nonexclusive, irrevocable,
+ * worldwide license in the Software to reproduce, prepare derivative
+ * works, and perform publicly and display publicly. Beginning five
+ * (5) years after the date permission to assert copyright is obtained
+ * from the U.S. Department of Energy, and subject to any subsequent
+ * five (5) year renewals, the U.S. Government is granted for itself
+ * and others acting on its behalf a paid-up, nonexclusive,
+ * irrevocable, worldwide license in the Software to reproduce,
+ * prepare derivative works, distribute copies to the public, perform
+ * publicly and display publicly, and to permit others to do so.
+ *
+ * This code is distributed under a BSD style license, see the LICENSE
+ * file for complete information.
+ */
+enum {
+ UNIT_LEN = 32
+};
+
+double unit_atof( const char *s );
+double unit_atof_rate( const char *s );
+iperf_size_t unit_atoi( const char *s );
+void unit_snprintf( char *s, int inLen, double inNum, char inFormat );
diff --git a/src/version.h.in b/src/version.h.in
new file mode 100644
index 0000000..627ef97
--- /dev/null
+++ b/src/version.h.in
@@ -0,0 +1,27 @@
+/*
+ * iperf, Copyright (c) 2014, 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.
+ *
+ * If you have questions about your rights to use or distribute this
+ * software, please contact Berkeley Lab's Technology Transfer
+ * Department at TTD@lbl.gov.
+ *
+ * NOTICE. This software is owned by the U.S. Department of Energy.
+ * As such, the U.S. Government has been granted for itself and others
+ * acting on its behalf a paid-up, nonexclusive, irrevocable,
+ * worldwide license in the Software to reproduce, prepare derivative
+ * works, and perform publicly and display publicly. Beginning five
+ * (5) years after the date permission to assert copyright is obtained
+ * from the U.S. Department of Energy, and subject to any subsequent
+ * five (5) year renewals, the U.S. Government is granted for itself
+ * and others acting on its behalf a paid-up, nonexclusive,
+ * irrevocable, worldwide license in the Software to reproduce,
+ * prepare derivative works, distribute copies to the public, perform
+ * publicly and display publicly, and to permit others to do so.
+ *
+ * This code is distributed under a BSD style license, see the LICENSE
+ * file for complete information.
+ */
+#define IPERF_VERSION "@PACKAGE_VERSION@"