diff options
Diffstat (limited to 'src')
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='[0;31m'; \ + grn='[0;32m'; \ + lgn='[1;32m'; \ + blu='[1;34m'; \ + mgn='[0;35m'; \ + brg='[1m'; \ + std='[m'; \ + 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@" |