diff --git a/README.md b/README.md index 356784b3b..afe27f654 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,14 @@ iperf3: A TCP, UDP, and SCTP network bandwidth measurement tool ================================================================ +NOTE +------ + +Modified by Candela Technologies to support compilation for Windows +(using mingw cross-compiler on Linux) and other bug fixes and features. + +https://github.com/greearb/iperf + Summary ------- @@ -54,6 +62,25 @@ None. (Note: If configure fails, try running `./bootstrap.sh` first) +### Building with mingw ### + autoconf; cross-configure.sh; cd src && make -f Makefile.mingw + +It will fail with mysterious errors if you do not copy these libraries from +the build machine to the install dir on the windows machine (possibly not all of these +are needed) + + Directory: C:\Users\Ben Greear\Desktop\iperf + +Mode LastWriteTime Length Name +---- ------------- ------ ---- +-a--- 10/20/2019 8:51 PM 326195 iperf3.exe +-a--- 10/20/2019 8:37 PM 1690887 libcrypto-10.dll +-a--- 10/20/2019 8:37 PM 1013208 libgcc_s_sjlj-1.dll +-a--- 10/20/2019 8:33 PM 67090 libgnurx-0.dll +-a--- 10/20/2019 8:35 PM 393870 libssl-10.dll +-a--- 10/20/2019 8:47 PM 62672 libwinpthread-1.dll + + Invoking iperf3 --------------- diff --git a/RELNOTES.md b/RELNOTES.md index ff02ba9ba..a75bb6327 100644 --- a/RELNOTES.md +++ b/RELNOTES.md @@ -1,6 +1,24 @@ iperf3 Release Notes ==================== +iperf 3.7-CT: Changes from upstream iperf3 + + * Support SO_BINDTODEVICE + + * Make sockets non-blocking to fix various ways the client and server can hang. + + * Server will recover from client doing bad things or dying unexpectedly. + + * Fix socket leaks + + * Report summary stats in all cases on both client and server. + + * Allow compiling for win32 using mingw cross-compiler. + + * Add lots of optionally-enabled debugging to help understand what is going on when + adding new features or debugging problems. + + iperf 3.7 2019-06-21 -------------------- diff --git a/configure b/configure index 217f1279d..3fcca43c6 100755 --- a/configure +++ b/configure @@ -12130,6 +12130,11 @@ CC=$lt_save_CC +case "$host" in + *-mingw*) + LDFLAGS="$LDFLAGS -lws2_32" +esac + # Add -Wall if we are using GCC. if test "x$GCC" = "xyes"; then CFLAGS="$CFLAGS -Wall" @@ -12390,7 +12395,7 @@ if test "$ac_res" != no; then : else echo "socket()" -exit 1 +#exit 1 fi @@ -12453,7 +12458,7 @@ if test "$ac_res" != no; then : else echo "inet_ntop()" -exit 1 +#exit 1 fi diff --git a/configure.ac b/configure.ac index d27af7d08..4c6e916ef 100644 --- a/configure.ac +++ b/configure.ac @@ -47,6 +47,11 @@ AC_PROG_RANLIB AC_PROG_LN_S AC_PROG_LIBTOOL +case "$host" in + *-mingw*) + LDFLAGS="$LDFLAGS -lws2_32" +esac + # Add -Wall if we are using GCC. if test "x$GCC" = "xyes"; then CFLAGS="$CFLAGS -Wall" @@ -74,13 +79,13 @@ exit 1 # On illumos we need -lsocket AC_SEARCH_LIBS(socket, [socket], [], [ echo "socket()" -exit 1 +#exit 1 ]) # On illumos inet_ntop in in -lnsl AC_SEARCH_LIBS(inet_ntop, [nsl], [], [ echo "inet_ntop()" -exit 1 +#exit 1 ]) # Checks for typedefs, structures, and compiler characteristics. diff --git a/docs/invoking.rst b/docs/invoking.rst index 37677424a..713087bf4 100644 --- a/docs/invoking.rst +++ b/docs/invoking.rst @@ -159,6 +159,10 @@ the executable. bind to the specific interface associated with address host. If the host has multiple interfaces, it will use the first inter- face by default. + + --bind_dev + bind to the specific network device. This calls SO_BINDTODEVICE, + and may require root permissions. -V, --verbose give more detailed output diff --git a/src/Makefile.mingw b/src/Makefile.mingw new file mode 100644 index 000000000..0f2b51f18 --- /dev/null +++ b/src/Makefile.mingw @@ -0,0 +1,77 @@ + +#CC = i386-mingw32-gcc +#CCC = i386-mingw32-g++ +CC = i686-w64-mingw32-gcc +CCC = i686-w64-mingw32-g++ +AR = i686-w64-mingw32-ar +RANLIB = i686-w64-mingw32-ranlib + + +# WINVER 0501 is XP (2003), and higher +# WINVER 0600 is Vista +BASIC_CCFLAGS = -g -O2 +CCFLAGS = $(BASIC_CCFLAGS) -DHAVE_SNPRINTF -DHAVE_VSNPRINTF \ + -DWINVER=0x0600 -D_WIN32_WINNT=0x0600 -DHAVE_REMOTE + +BASIC_LDLIBS=-lws2_32 +LDLIBS = $(BASIC_LDLIBS) -liphlpapi -lssl -lcrypto + +LDFLAGS := $(LDFLAGS) + +# Other main classes +# t_api.c t_timer.c t_units.c t_uuid.c + +# makefile template +COMMON_SRCS = cjson.c iperf_auth.c iperf_locale.c iperf_tcp.c \ + iperf_util.c units.c \ + dscp.c iperf_client_api.c iperf_sctp.c iperf_time.c main.c tcp_info.c \ + iperf_api.c iperf_error.c iperf_server_api.c iperf_udp.c net.c timer.c + +COMMON_OBJS = cjson.o iperf_auth.o iperf_locale.o iperf_tcp.o \ + iperf_util.o units.o \ + dscp.o iperf_client_api.o iperf_sctp.o iperf_time.o main.o tcp_info.o \ + iperf_api.o iperf_error.o iperf_server_api.o iperf_udp.o net.o timer.o + + +TARG = iperf3.exe + +ALL_OBJS=${COMMON_OBJS} +ALL_SRCS=${COMMON_SRCS} + +${TARG}: $(COMMON_OBJS) + $(CC) $(CCFLAGS) $(LDFLAGS) -o ${TARG} $(COMMON_OBJS)\ + $(LDLIBS) + +$(ALL_OBJS): %.o: %.c Makefile.mingw + @echo " " + @echo "Making object file $<" + $(CC) $(CCFLAGS) -c $< + +package_win32: ${TARG} + # Copy libraries into place so we can package them. + cp /usr/i686-w64-mingw32/sys-root/mingw/bin/libwinpthread-1.dll ./ + cp /usr/i686-w64-mingw32/sys-root/mingw/bin/libssl-10.dll ./ + cp /usr/i686-w64-mingw32/sys-root/mingw/bin/libcrypto-10.dll ./ + cp /usr/i686-w64-mingw32/sys-root/mingw/bin/libgcc_s_sjlj-1.dll ./ + cp /usr/i686-w64-mingw32/sys-root/mingw/bin/libgnurx-0.dll ./ + cp /usr/i686-w64-mingw32/sys-root/mingw/bin/zlib1.dll ./ + cp ../LICENSE ./license.txt + makensis iperf.nsis + +clean: + rm -f ${ALL_OBJS} + +purge: clean + rm -f *~ *.flc *lo *.o *.la ${TARG} \ + make.depend *.dll *.exe + +ifneq "$(PURGEMEHARDER)" "1" +# Dependencies are supposed to be in a file ``make.depend'' +# which is inclduded by make. +include make.depend +# Because of the following rule, ``make'' will attempt to +# create ``make.depend'' if it does not exist or if one +# of the files in $(COMMON_SRCS) is more recent than ``make.depend'' +make.depend: $(ALL_SRCS) Makefile.mingw + $(CC) -M $(CCFLAGS) $(ALL_SRCS) > make.depend +endif diff --git a/src/cjson.c b/src/cjson.c index a31874dbf..cd4034472 100644 --- a/src/cjson.c +++ b/src/cjson.c @@ -372,7 +372,7 @@ static unsigned char* ensure(printbuffer * const p, size_t needed) /* overflow of int, use LLONG_MAX if possible */ if (needed <= LLONG_MAX) { - newsize = LLONG_MAX; + newsize = (size_t)LLONG_MAX; } else { diff --git a/src/dscp.c b/src/dscp.c index 329b3043d..d3f03bef5 100644 --- a/src/dscp.c +++ b/src/dscp.c @@ -31,7 +31,7 @@ #include #ifdef WIN32 -#define strcasecmp(a,b) _stricmp(a,b) +//#define strcasecmp(a,b) _stricmp(a,b) #define snprintf _snprintf #endif diff --git a/src/iperf.h b/src/iperf.h index 6ce77f59b..a0a7e5d05 100755 --- a/src/iperf.h +++ b/src/iperf.h @@ -29,17 +29,80 @@ #include "iperf_config.h" +#ifndef __WIN32__ +#include +#include +#include +#include +#include +#include +#include +#include // for IFNAMSIZ +#include +#include + +#define STRERROR strerror(errno) +#define ERRNO errno + +#else +#include +#include +typedef int socklen_t; + +const char* winstrerror(); +#define STRERROR winstrerror() +#define ERRNO WSAGetLastError() + +/* From here: https://github.com/eclipse/paho.mqtt.c/issues/577 */ +#define htonll(x) ((1==htonl(1)) ? (x) : ((((uint64_t)htonl((x)) & 0xFFFFFFFFUL)) << 32) | ((uint64_t)(htonl((uint32_t)((x)))) >> 32)) +#define ntohll(x) ((1==ntohl(1)) ? (x) : ((((uint64_t)ntohl((x)) & 0xFFFFFFFFUL)) << 32) | ((uint64_t)(ntohl((uint32_t)((x)))) >> 32)) + +#define LODWORD(l) ((DWORD)((DWORDLONG)(l))) +#define HIDWORD(l) ((DWORD)(((DWORDLONG)(l)>>32)&0xFFFFFFFF)) + +/* implementation of mman for windows */ +#ifndef SYS_MMAN_H +#define SYS_MMAN_H + +#define PROT_NONE 0x00 /* No access. */ +#define PROT_READ 0x04 /* Pages can be read. */ +#define PROT_WRITE 0x02 /* Pages can be written. */ +#define PROT_EXEC 0x01 /* Pages can be executed. */ + +/* Flags contain mapping type, sharing type and options. */ + +/* Mapping type (must choose one and only one of these). */ +#define MAP_FILE 0x0001 /* Mapped from a file or device. */ +#define MAP_ANON 0x0002 /* Allocated from anonymous virtual memory. */ +#define MAP_TYPE 0x000f /* Mask for type field. */ +#define MAP_ANONYMOUS MAP_ANON /* Linux name. */ + +/* Sharing types (must choose one and only one of these). */ +#define MAP_COPY 0x0020 /* Virtual copy of region at mapping time. */ +#define MAP_SHARED 0x0010 /* Share changes. */ +#define MAP_PRIVATE 0x0000 /* Changes private; copy pages on write. */ + +/* Other flags. */ +#define MAP_FIXED 0x0100 /* Map address must be exactly as requested. */ +#define MAP_NOEXTEND 0x0200 /* For MAP_FILE, don't change file size. */ +#define MAP_HASSEMPHORE 0x0400 /* Region may contain semaphores. */ +#define MAP_INHERIT 0x0800 /* Region is retained after exec. */ + +#define MAP_FAILED ((void *)-1) + +void *mmap (void *, size_t, int, int, int, off_t); +int munmap (void *, size_t); +#endif +#endif + #include #include #ifdef HAVE_STDINT_H #include #endif -#include -#include #ifndef _GNU_SOURCE # define _GNU_SOURCE #endif -#include #if defined(HAVE_CPUSET_SETAFFINITY) #include @@ -135,6 +198,7 @@ struct iperf_settings int domain; /* AF_INET or AF_INET6 */ int socket_bufsize; /* window size for TCP */ int blksize; /* size of read/writes (-l) */ + int buflen; /* read/write buffer len, for tcp rcv, can be larger than blksize */ uint64_t rate; /* target data rate for application pacing*/ uint64_t fqrate; /* target data rate for FQ pacing*/ int pacing_timer; /* pacing timer in microseconds */ @@ -146,6 +210,7 @@ struct iperf_settings iperf_size_t bytes; /* number of bytes to send */ iperf_size_t blocks; /* number of blocks (packets) to send */ char unit_format; /* -f */ + int unit_precision; /* --precision */ int num_ostreams; /* SCTP initmsg settings */ #if defined(HAVE_SSL) char *authtoken; /* Authentication token */ @@ -166,7 +231,7 @@ struct iperf_stream int local_port; int remote_port; int socket; - int id; + int stream_id; int sender; /* XXX: is settings just a pointer to the same struct in iperf_test? if not, should it be? */ @@ -252,6 +317,7 @@ struct iperf_test 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; @@ -316,6 +382,8 @@ struct iperf_test int done; Timer *stats_timer; Timer *reporter_timer; + uint64_t create_streams_state_at; /* when did we enter the create-streams state? */ + uint64_t done_at_ms; 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 */ diff --git a/src/iperf.nsis b/src/iperf.nsis new file mode 100644 index 000000000..337c501fe --- /dev/null +++ b/src/iperf.nsis @@ -0,0 +1,273 @@ +; iper3-ct install script for NSIS. --Ben Greear +RequestExecutionLevel highest + +!include "MUI2.nsh" +!include "InstallOptions.nsh" + +; ------------------------------- +; Start + +!define LFS_VER 3.7-ct +!define MUI_PRODUCT "iperf3-ct {LFS_VER}" +!define LF_INSTALL_DIR "iperf3-ct" + +CRCCheck On + +;-------------------------------- +;General + +Name "iperf3-ct ${LFS_VER} Installer" +OutFile "iperf3-ct-${LFS_VER}-installer.exe" + +ShowInstDetails "hide" +ShowUninstDetails "hide" +SetCompressor "lzma" +; install icon +;!define MUI_ICON "Installer-box-server-256.ico" +; server desktop icon +;!define DT_ICON "brass_anvil_lh_server.ico" +; server lfconfig icon +;!define CF_ICON "brass_anvil_lh_config.ico" +; uninstaller icon +;!define MUI_UNICON ${MUI_ICON} + +; !define MUI_HEADERIMAGE +; !define MUI_HEADERIMAGE_RIGHT +; !define MUI_HEADERIMAGE_BITMAP "candela_swirl_small-57h.bmp" +; !define MUI_HEADERIMAGE_UNBITMAP "candela_swirl_small-57h.bmp" + +;-------------------------------- +;Folder selection page + +InstallDir "$PROGRAMFILES\${LF_INSTALL_DIR}" +InstallDirRegKey HKCU "Software\iperf3-ct" "" + + +;-------------------------------- +;Modern UI Configuration +!define MUI_ABORTWARNING +!define MUI_UNINSTALLER +!define MUI_WELCOMEPAGE + +!insertmacro MUI_PAGE_WELCOME +!insertmacro MUI_PAGE_LICENSE "license.txt" +!insertmacro MUI_PAGE_COMPONENTS +!insertmacro MUI_PAGE_DIRECTORY +!insertmacro MUI_PAGE_INSTFILES + +!insertmacro MUI_UNPAGE_WELCOME +!insertmacro MUI_UNPAGE_COMPONENTS +!insertmacro MUI_UNPAGE_CONFIRM +!insertmacro MUI_UNPAGE_INSTFILES +!insertmacro MUI_UNPAGE_FINISH + +!insertmacro MUI_UNPAGE_INSTFILES +!insertmacro MUI_LANGUAGE "English" + +!define SMP_INST "$SMPROGRAMS\${LF_INSTALL_DIR}" + + +; ! macro plugin_init +; !echo "${NSISDIR}\Plugins\x86-unicode\InstallOptions.dll" +; !if /FileExists '"${NSISDIR}\Plugins\x86-unicode\InstallOptions.dll"' +!if `"${NSISDIR}\Plugins\x86-unicode\InstallOptions.dll"` + !echo "trying try_unicode_install_opts" + !define found_install_opts "${NSISDIR}\Plugins\x86-unicode\InstallOptions.dll" +!elseif /FileExists "${NSISDIR}\Plugins\x86-ansi\InstallOptions.dll" + !echo "trying try_ansi_install_opts" + !define found_install_opts "${NSISDIR}\Plugins\x86-ansi\InstallOptions.dll" +!elseif /FileExists "${NSISDIR}\Plugins\InstallOptions.dll" + !echo "trying old_install_opts: $found_install_opts" + !define found_install_opts "${NSISDIR}\Plugins\InstallOptions.dll" +!endif + +!ifdef found_install_opts + !echo "installoptions.dll: ${found_install_opts}" + ;ReserveFile /plugin "${found_install_opts}" +!else + !echo "no installoptions.dll $found_install_opts" +!endif +; ! macroend + + +Function .onInit + InitPluginsDir + + !ifdef found_install_opts + !echo "found_install_opts: ${found_install_opts}" + ReserveFile /plugin "${found_install_opts}" + !endif +FunctionEnd + + +;-------------------------------- +;Installer Sections +Section "install" Installation + +;Add files + SetOutPath "$INSTDIR" + +; Remove some old .dll files that could conflict with new +; installs (for instance, if we are using non-OEM winpcap) +; Delete "$INSTDIR\packet.dll" + + File "iperf3.exe" +; File ${MUI_ICON} +; File ${DT_ICON} +; File ${CF_ICON} + File "libwinpthread-1.dll" + File "libssl-10.dll" + File "libcrypto-10.dll" + File "libgcc_s_sjlj-1.dll" + File "libgnurx-0.dll" + File "zlib1.dll" + +;write uninstall information to the registry + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${LF_INSTALL_DIR}" "DisplayName" "${MUI_PRODUCT} (remove only)" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${LF_INSTALL_DIR}" "UninstallString" "$INSTDIR\Uninstall.exe" + + WriteUninstaller "$INSTDIR\Uninstall.exe" + +SectionEnd + + +;-------------------------------- +;Uninstaller Section +Section "Uninstall" + +;Delete Files + RMDir /r "$INSTDIR\*.*" + +;Remove the installation directory + RMDir "$INSTDIR" + +;Delete Uninstaller And Unistall Registry Entries + DeleteRegKey HKLM "SOFTWARE\${LF_INSTALL_DIR}" + DeleteRegKey HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\${LF_INSTALL_DIR}" + +SectionEnd + +;Function that calls a messagebox when installation finished correctly +Function .onInstSuccess + MessageBox MB_OK \ + '${MUI_PRODUCT} was installed. Change to the install directory and run .\iperf3.exe --help for usage help' +FunctionEnd + +Function un.onUninstSuccess + ;MessageBox MB_OK "You have successfully uninstalled ${MUI_PRODUCT}." +FunctionEnd + +;------------------------------------------------------------------------------ +; FindWindowClose +; +; Closes a window if open. Also prompts user if closing fails. +; Use by passing the window class and title on the stack. +; You must pass both even if one is empty (i.e. ""). +; +; Usage: +; Push ThunderRT6FormDC +; Push "Visual Basic Form Name" +; Call FindWindowClose +; + +Function FindWindowClose + Exch $0 + Exch + Exch $1 + Push $2 + Push $3 + find: + FindWindow $2 $1 $0 + IntCmp $2 0 nowindow + MessageBox MB_YESNO "$0 must be stopped before continuing install. Would you like it stopped now?" IDNO nowindow + SendMessage $2 16 "" "" + Sleep 500 + FindWindow $2 $1 $0 + IntCmp $2 0 nowindow + MessageBox MB_OK|MB_ICONSTOP "An instance of the program $0 is running. Please close it and press OK to continue." + Goto find + nowindow: + ;MessageBox MB_OK "Program $0 is not running." + Pop $3 + Pop $2 + Pop $1 + Pop $0 +FunctionEnd + + +Function un.confirm_again + MessageBox MB_YESNO "Do you really want to uninstall ${LFS_VER} now?" IDYES dont_abt + Abort + dont_abt: +FunctionEnd + +;------------------------------------------------------------------------------ +; GetWindowsVersion +; +; Based on Yazno's function, http://yazno.tripod.com/powerpimpit/ +; Returns on top of stack +; +; Windows Version (95, 98, ME, NT x.x, 2000) +; or +; '' (Unknown Windows Version) +; +; Usage: +; Call GetWindowsVersion +; Pop $0 +; ; at this point $0 is "NT 4.0" or whatnot + +Function GetWindowsVersion + Push $0 + Push $9 + ReadRegStr $0 HKLM "SOFTWARE\Microsoft\Windows NT\CurrentVersion" CurrentVersion + StrCmp $0 "" 0 lbl_winnt + ; we are not NT. + ReadRegStr $0 HKLM SOFTWARE\Microsoft\Windows\CurrentVersion VersionNumber + + StrCpy $9 $0 1 + StrCmp $9 '4' 0 lbl_error + + StrCpy $9 $0 3 + + StrCmp $9 '4.0' lbl_win32_95 + StrCmp $9 '4.9' lbl_win32_ME lbl_win32_98 + + lbl_win32_95: + StrCpy $0 '95' + Goto lbl_done + + lbl_win32_98: + StrCpy $0 '98' + Goto lbl_done + + lbl_win32_ME: + StrCpy $0 'ME' + Goto lbl_done + + lbl_winnt: + + StrCpy $9 $0 1 + StrCmp $9 '3' lbl_winnt_x + StrCmp $9 '4' lbl_winnt_x + StrCmp $9 '5' lbl_winnt_5 lbl_error + + lbl_winnt_x: + StrCpy $0 "NT $0" 6 + Goto lbl_done + + lbl_winnt_5: + StrCmp $0 "5.1" lbl_wxp + Strcpy $0 '2000' + Goto lbl_done + lbl_wxp: + Strcpy $0 'XP' + Goto lbl_done + + lbl_error: + Strcpy $0 '' + lbl_done: + Pop $9 + Exch $0 +FunctionEnd + +; eof diff --git a/src/iperf3.1 b/src/iperf3.1 index 731855e2e..ac8c156c1 100644 --- a/src/iperf3.1 +++ b/src/iperf3.1 @@ -153,6 +153,9 @@ send output to a log file. force flushing output at every interval. Used to avoid buffering when sending output to pipe. .TP +.BR -I ", " --pidfile " \fIfile\fR" +write a file with the process ID, useful for automated process cleanup. +.TP .BR -d ", " --debug " " emit debugging output. Primarily (perhaps exclusively) of use to developers. @@ -171,9 +174,6 @@ run in server mode .BR -D ", " --daemon " " run the server in background as a daemon .TP -.BR -I ", " --pidfile " \fIfile\fR" -write a file with the process ID, most useful when running as a daemon. -.TP .BR -1 ", " --one-off handle one client connection, then exit. .TP diff --git a/src/iperf_api.c b/src/iperf_api.c index 3b7e45ef6..7e94594c3 100755 --- a/src/iperf_api.c +++ b/src/iperf_api.c @@ -41,18 +41,11 @@ #include #include #include -#include #include -#include -#include -#include #ifdef HAVE_STDINT_H #include #endif -#include #include -#include -#include #include #include #include @@ -94,9 +87,9 @@ 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 int JSON_write(int fd, cJSON *json, struct iperf_test *test); static void print_interval_results(struct iperf_test *test, struct iperf_stream *sp, cJSON *json_interval_streams); -static cJSON *JSON_read(int fd); +static cJSON *JSON_read(int fd, struct iperf_test *test); /*************************** Print usage functions ****************************/ @@ -115,12 +108,6 @@ usage_long(FILE *f) } -void warning(char *str) -{ - fprintf(stderr, "warning: %s\n", str); -} - - /************** Getter routines for some fields inside iperf_test *************/ int @@ -303,6 +290,12 @@ 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) { @@ -388,6 +381,8 @@ void iperf_set_test_blksize(struct iperf_test *ipt, int blksize) { ipt->settings->blksize = blksize; + if (ipt->settings->buflen < blksize) + ipt->settings->buflen = blksize; } void @@ -583,6 +578,12 @@ iperf_set_test_bind_address(struct iperf_test *ipt, char *bnd_address) ipt->bind_address = strdup(bnd_address); } +void +iperf_set_test_bind_dev(struct iperf_test *ipt, char *bnd_dev) +{ + ipt->bind_dev = strdup(bnd_dev); +} + void iperf_set_test_udp_counters_64bit(struct iperf_test *ipt, int udp_counters_64bit) { @@ -769,7 +770,7 @@ iperf_on_connect(struct iperf_test *test) } } if (test->settings->rate) - iperf_printf(test, " Target Bitrate: %llu\n", test->settings->rate); + iperf_printf(test, " Target Bitrate: %llu\n", (unsigned long long)test->settings->rate); } } @@ -788,6 +789,7 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv) { {"port", required_argument, NULL, 'p'}, {"format", required_argument, NULL, 'f'}, + {"precision", required_argument, NULL, 'r'}, {"interval", required_argument, NULL, 'i'}, {"daemon", no_argument, NULL, 'D'}, {"one-off", no_argument, NULL, '1'}, @@ -808,6 +810,7 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv) {"bidir", no_argument, NULL, OPT_BIDIRECTIONAL}, {"window", required_argument, NULL, 'w'}, {"bind", required_argument, NULL, 'B'}, + {"bind_dev", required_argument, NULL, OPT_BIND_DEV}, {"cport", required_argument, NULL, OPT_CLIENT_PORT}, {"set-mss", required_argument, NULL, 'M'}, {"no-delay", no_argument, NULL, 'N'}, @@ -873,7 +876,7 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv) char *client_username = NULL, *client_rsa_public_key = NULL, *server_rsa_private_key = NULL; #endif /* HAVE_SSL */ - while ((flag = getopt_long(argc, argv, "p:f:i:D1VJvsc:ub:t:n:k:l:P:Rw:B:M:N46S:L:ZO:F:A:T:C:dI:hX:", longopts, NULL)) != -1) { + while ((flag = getopt_long(argc, argv, "p:f:i:D1VJvsc:ub:t:n:k:l:P:Rr:w:B:M:N46S:L:ZO:F:A:T:C:dI:hX:", longopts, NULL)) != -1) { switch (flag) { case 'p': portno = atoi(optarg); @@ -883,6 +886,13 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv) } test->server_port = portno; break; + case 'r': + test->settings->unit_precision = atoi(optarg); + if (test->settings->unit_precision < -1) + test->settings->unit_precision = 1; + else if (test->settings->unit_precision > 30) + test->settings->unit_precision = 30; + break; case 'f': if (!optarg) { i_errno = IEBADFORMAT; @@ -1044,6 +1054,9 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv) case 'B': test->bind_address = strdup(optarg); break; + case OPT_BIND_DEV: + test->bind_dev = strdup(optarg); + break; case OPT_CLIENT_PORT: portno = atoi(optarg); if (portno < 1 || portno > 65535) { @@ -1179,11 +1192,10 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv) #endif /* HAVE_TCP_CONGESTION */ break; case 'd': - test->debug = 1; + test->debug++; break; case 'I': test->pidfile = strdup(optarg); - server_flag = 1; break; case OPT_LOGFILE: test->logfile = strdup(optarg); @@ -1243,6 +1255,8 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv) usage_long(stdout); exit(0); default: + printf("arg: %d (%c)\n", + (int)flag, (char)flag); usage_long(stderr); exit(1); } @@ -1333,6 +1347,10 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv) return -1; } test->settings->blksize = blksize; + if (blksize < DEFAULT_TCP_BLKSIZE) + test->settings->buflen = DEFAULT_TCP_BLKSIZE; + else + test->settings->buflen = blksize; if (!rate_flag) test->settings->rate = test->protocol->id == Pudp ? UDP_RATE : 0; @@ -1365,15 +1383,15 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv) /* 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)"); + iperf_err(test, "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)"); + iperf_err(test, "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)"); + iperf_err(test, "Debug output (-d) may interfere with JSON output (-J)"); } return 0; @@ -1393,11 +1411,21 @@ int iperf_open_logfile(struct iperf_test *test) return 0; } +void iperf_set_state(struct iperf_test *test, signed char state, const char* dbg) +{ + if (test->debug) { + iperf_err(test, "test: %p state: %d(%s) ==> %d(%s) dbg: %s\n", + test, test->state, iperf_get_state_str(test->state), + state, iperf_get_state_str(state), dbg); + } + test->state = state; +} + int iperf_set_send_state(struct iperf_test *test, signed char state) { - test->state = state; - if (Nwrite(test->ctrl_sck, (char*) &state, sizeof(state), Ptcp) < 0) { + iperf_set_state(test, state, __FUNCTION__); + if (waitWrite(test->ctrl_sck, (char*) &state, sizeof(state), Ptcp, test, ctrl_wait_ms) != sizeof(state)) { i_errno = IESENDMESSAGE; return -1; } @@ -1418,10 +1446,10 @@ iperf_check_throttle(struct iperf_stream *sp, struct iperf_time *nowP) bits_per_second = sp->result->bytes_sent * 8 / seconds; if (bits_per_second < sp->test->settings->rate) { sp->green_light = 1; - FD_SET(sp->socket, &sp->test->write_set); + IFD_SET(sp->socket, &sp->test->write_set, sp->test); } else { sp->green_light = 0; - FD_CLR(sp->socket, &sp->test->write_set); + IFD_CLR(sp->socket, &sp->test->write_set, sp->test); } } @@ -1473,8 +1501,9 @@ iperf_send(struct iperf_test *test, fd_set *write_setP) } if (write_setP != NULL) SLIST_FOREACH(sp, &test->streams, streams) - if (FD_ISSET(sp->socket, write_setP)) - FD_CLR(sp->socket, write_setP); + if (sp->socket >= 0) + if (FD_ISSET(sp->socket, write_setP)) + IFD_CLR(sp->socket, write_setP, test); return 0; } @@ -1482,21 +1511,31 @@ iperf_send(struct iperf_test *test, fd_set *write_setP) int iperf_recv(struct iperf_test *test, fd_set *read_setP) { + int valid_sock = 0; int r; struct iperf_stream *sp; SLIST_FOREACH(sp, &test->streams, streams) { + if (sp->socket < 0) + continue; + valid_sock++; if (FD_ISSET(sp->socket, read_setP) && !sp->sender) { if ((r = sp->rcv(sp)) < 0) { - i_errno = IESTREAMREAD; - return r; + iperf_err(test, "Failed rcv: %s socket: %d", STRERROR, sp->socket); + iclosesocket(sp->socket, test); + sp->socket = -1; + valid_sock--; } - test->bytes_received += r; - ++test->blocks_received; - FD_CLR(sp->socket, read_setP); + else { + test->bytes_received += r; + ++test->blocks_received; + /* IFD_CLR(sp->socket, read_setP, test); // Don't see how this is helpful. --Ben */ + } } } + if (valid_sock == 0) + return -1; return 0; } @@ -1617,7 +1656,7 @@ iperf_exchange_parameters(struct iperf_test *test) return -1; i_errno = IEAUTHTEST; err = htonl(i_errno); - if (Nwrite(test->ctrl_sck, (char*) &err, sizeof(err), Ptcp) < 0) { + if (waitWrite(test->ctrl_sck, (char*) &err, sizeof(err), Ptcp, test, ctrl_wait_ms) != sizeof(err)) { i_errno = IECTRLWRITE; return -1; } @@ -1626,25 +1665,30 @@ iperf_exchange_parameters(struct iperf_test *test) #endif //HAVE_SSL if ((s = test->protocol->listen(test)) < 0) { - if (iperf_set_send_state(test, SERVER_ERROR) != 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) { + if (waitWrite(test->ctrl_sck, (char*) &err, sizeof(err), Ptcp, test, ctrl_wait_ms) != sizeof(err)) { i_errno = IECTRLWRITE; return -1; } err = htonl(errno); - if (Nwrite(test->ctrl_sck, (char*) &err, sizeof(err), Ptcp) < 0) { + if (waitWrite(test->ctrl_sck, (char*) &err, sizeof(err), Ptcp, test, ctrl_wait_ms) != sizeof(err)) { i_errno = IECTRLWRITE; return -1; } return -1; } - FD_SET(s, &test->read_set); - test->max_fd = (s > test->max_fd) ? s : test->max_fd; + + if (test->debug) { + iperf_err(test, "iperf-exchange-parameters, setting read FD: %d.\n", s); + } + + IFD_SET(s, &test->read_set, test); test->prot_listener = s; // Send the control message to create streams and start the test + test->create_streams_state_at = getCurMs(); if (iperf_set_send_state(test, CREATE_STREAMS) != 0) return -1; @@ -1653,6 +1697,46 @@ iperf_exchange_parameters(struct iperf_test *test) return 0; } +void _fd_set(int fd, fd_set* fdset, struct iperf_test *test, const char* file, int line) { + if (test->debug > 1) { + iperf_err(test, "FD-SET, fd: %d at %s:%d", + fd, file, line); + } + FD_SET(fd, fdset); + if (fd > test->max_fd) + test->max_fd = fd; +} + +void _fd_clr(int fd, fd_set* fdset, struct iperf_test *test, const char* file, int line) { + if (test->debug > 1) { + iperf_err(test, "FD-CLR, fd: %d at %s:%d", + fd, file, line); + } + FD_CLR(fd, fdset); +} + +const char* iperf_get_state_str(int s) { + switch (s) { + case TEST_INIT: return "INIT"; + case TEST_START: return "START"; + case TEST_RUNNING: return "RUNNING"; + case TEST_END: return "END"; + case PARAM_EXCHANGE: return "PARAM-EXCHANGE"; + case STREAM_RUNNING: return "STREAM-RUNNING"; + case CREATE_STREAMS: return "CREATE-STREAMS"; + case SERVER_TERMINATE: return "SERVER-TERMINATE"; + case CLIENT_TERMINATE: return "CLIENT-TERMINATE"; + case EXCHANGE_RESULTS: return "EXCHANGE-RESULTS"; + case DISPLAY_RESULTS: return "DISPLAY-RESULTS"; + case IPERF_START: return "IPERF-START"; + case IPERF_DONE: return "IPERF-DONE"; + case ACCESS_DENIED: return "ACCESS-DENIED"; + case SERVER_ERROR: return "SERVER-ERROR"; + default: + return "UNKNOWN"; + } +} + /*************************************************************/ int @@ -1751,10 +1835,12 @@ send_parameters(struct iperf_test *test) cJSON_AddStringToObject(j, "client_version", IPERF_VERSION); if (test->debug) { - printf("send_parameters:\n%s\n", cJSON_Print(j)); + char* jp = cJSON_Print(j); + printf("send_parameters:\n%s\n", jp); + free(jp); } - if (JSON_write(test->ctrl_sck, j) < 0) { + if (JSON_write(test->ctrl_sck, j, test) < 0) { i_errno = IESENDPARAMS; r = -1; } @@ -1772,7 +1858,7 @@ get_parameters(struct iperf_test *test) cJSON *j; cJSON *j_p; - j = JSON_read(test->ctrl_sck); + j = JSON_read(test->ctrl_sck, test); if (j == NULL) { i_errno = IERECVPARAMS; r = -1; @@ -1929,7 +2015,7 @@ send_results(struct iperf_test *test) 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, "id", sp->stream_id); cJSON_AddNumberToObject(j_stream, "bytes", bytes_transferred); cJSON_AddNumberToObject(j_stream, "retransmits", retransmits); cJSON_AddNumberToObject(j_stream, "jitter", sp->jitter); @@ -1950,7 +2036,7 @@ send_results(struct iperf_test *test) printf("send_results\n%s\n", str); free(str); } - if (r == 0 && JSON_write(test->ctrl_sck, j) < 0) { + if (r == 0 && JSON_write(test->ctrl_sck, j, test) < 0) { i_errno = IESENDRESULTS; r = -1; } @@ -1990,7 +2076,7 @@ get_results(struct iperf_test *test) int retransmits; struct iperf_stream *sp; - j = JSON_read(test->ctrl_sck); + j = JSON_read(test->ctrl_sck, test); if (j == NULL) { i_errno = IERECVRESULTS; r = -1; @@ -2051,7 +2137,8 @@ get_results(struct iperf_test *test) cerror = j_errors->valueint; pcount = j_packets->valueint; SLIST_FOREACH(sp, &test->streams, streams) - if (sp->id == sid) break; + if (sp->stream_id == sid) + break; if (sp == NULL) { i_errno = IESTREAMID; r = -1; @@ -2125,7 +2212,7 @@ get_results(struct iperf_test *test) /*************************************************************/ static int -JSON_write(int fd, cJSON *json) +JSON_write(int fd, cJSON *json, struct iperf_test *test) { uint32_t hsize, nsize; char *str; @@ -2137,10 +2224,10 @@ JSON_write(int fd, cJSON *json) else { hsize = strlen(str); nsize = htonl(hsize); - if (Nwrite(fd, (char*) &nsize, sizeof(nsize), Ptcp) < 0) + if (waitWrite(fd, (char*) &nsize, sizeof(nsize), Ptcp, test, ctrl_wait_ms) < 0) r = -1; else { - if (Nwrite(fd, str, hsize, Ptcp) < 0) + if (waitWrite(fd, str, hsize, Ptcp, test, ctrl_wait_ms) != hsize) r = -1; } free(str); @@ -2151,7 +2238,7 @@ JSON_write(int fd, cJSON *json) /*************************************************************/ static cJSON * -JSON_read(int fd) +JSON_read(int fd, struct iperf_test *test) { uint32_t hsize, nsize; char *str; @@ -2163,12 +2250,12 @@ JSON_read(int fd) * 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) { + if (waitRead(fd, (char*) &nsize, sizeof(nsize), Ptcp, test, ctrl_wait_ms) == sizeof(nsize)) { hsize = ntohl(nsize); /* Allocate a buffer to hold the JSON */ str = (char *) calloc(sizeof(char), hsize+1); /* +1 for trailing null */ if (str != NULL) { - rc = Nread(fd, str, hsize, Ptcp); + rc = waitRead(fd, str, hsize, Ptcp, test, ctrl_wait_ms); if (rc >= 0) { /* * We should be reading in the number of bytes corresponding to the @@ -2183,8 +2270,11 @@ JSON_read(int fd) printf("WARNING: Size of data read does not correspond to offered length\n"); } } + else { + iperf_err(test, "WARNING: Error waiting for json read, hsize: %d, errno: %s", hsize, STRERROR); + } + free(str); } - free(str); } return json; } @@ -2236,9 +2326,9 @@ connect_msg(struct iperf_stream *sp) } 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)); + cJSON_AddItemToArray(sp->test->json_connected, iperf_json_printf("id: %d socket: %d local_host: %s local_port: %d remote_host: %s remote_port: %d", (int64_t)sp->stream_id, (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); + iperf_printf(sp->test, report_connected, sp->stream_id, ipl, lport, ipr, rport); } @@ -2329,6 +2419,7 @@ iperf_defaults(struct iperf_test *testp) testp->settings->domain = AF_UNSPEC; testp->settings->unit_format = 'a'; + testp->settings->unit_precision = -1; /* auto */ testp->settings->socket_bufsize = 0; /* use autotuning */ testp->settings->blksize = DEFAULT_TCP_BLKSIZE; testp->settings->rate = 0; @@ -2429,6 +2520,8 @@ iperf_free_test(struct iperf_test *test) 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; @@ -2572,7 +2665,9 @@ iperf_reset_test(struct iperf_test *test) #if defined(HAVE_CPUSET_SETAFFINITY) CPU_ZERO(&test->cpumask); #endif /* HAVE_CPUSET_SETAFFINITY */ - test->state = 0; + iperf_set_state(test, TEST_INIT, __FUNCTION__); + test->create_streams_state_at = 0; + test->done_at_ms = 0; test->ctrl_sck = -1; test->prot_listener = -1; @@ -2591,6 +2686,7 @@ iperf_reset_test(struct iperf_test *test) FD_ZERO(&test->read_set); FD_ZERO(&test->write_set); + test->max_fd = 0; test->num_streams = 1; test->settings->socket_bufsize = 0; @@ -2660,6 +2756,7 @@ iperf_reset_stats(struct iperf_test *test) sp->omitted_cnt_error = sp->cnt_error; sp->omitted_outoforder_packets = sp->outoforder_packets; sp->jitter = 0; + sp->prev_transit = 0; rp = sp->result; rp->bytes_sent_omit = rp->bytes_sent; rp->bytes_received = 0; @@ -2907,9 +3004,9 @@ iperf_print_intermediate(struct iperf_test *test) if (sp) { irp = TAILQ_LAST(&sp->result->interval_results, irlisthead); /* use 1st stream for timing info */ - unit_snprintf(ubuf, UNIT_LEN, (double) bytes, 'A'); + unit_snprintf(ubuf, UNIT_LEN, (double) bytes, 'A', test->settings->unit_precision); bandwidth = (double) bytes / (double) irp->interval_duration; - unit_snprintf(nbuf, UNIT_LEN, bandwidth, test->settings->unit_format); + unit_snprintf(nbuf, UNIT_LEN, bandwidth, test->settings->unit_format, test->settings->unit_precision); iperf_time_diff(&sp->result->start_time,&irp->interval_start_time, &temp_time); start_time = iperf_time_in_secs(&temp_time); @@ -3133,39 +3230,44 @@ iperf_print_results(struct iperf_test *test) avg_jitter += sp->jitter; } - unit_snprintf(ubuf, UNIT_LEN, (double) bytes_sent, 'A'); + unit_snprintf(ubuf, UNIT_LEN, (double) bytes_sent, 'A', test->settings->unit_precision); + 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); + unit_snprintf(nbuf, UNIT_LEN, bandwidth, test->settings->unit_format, test->settings->unit_precision); 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, "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_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_rtt, (int64_t) sp->result->stream_min_rtt, (int64_t) ((sp->result->stream_count_rtt == 0) ? 0 : sp->result->stream_sum_rtt / sp->result->stream_count_rtt), stream_must_be_sender)); + cJSON_AddItemToObject(json_summary_stream, "sender", + iperf_json_printf("id: %d socket: %d start: %f end: %f seconds: %f bytes: %d bits_per_second: %f retransmits: %d max_snd_cwnd: %d max_rtt: %d min_rtt: %d mean_rtt: %d sender: %b", + (int64_t) sp->stream_id, + (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_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); - } + iperf_printf(test, report_bw_retrans_format, sp->stream_id, 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, "sender", iperf_json_printf("socket: %d start: %f end: %f seconds: %f bytes: %d bits_per_second: %f sender: %b", (int64_t) sp->socket, (double) start_time, (double) sender_time, (double) sender_time, (int64_t) bytes_sent, bandwidth * 8, stream_must_be_sender)); + cJSON_AddItemToObject(json_summary_stream, "sender", + iperf_json_printf("id: %d socket: %d start: %f end: %f seconds: %f bytes: %d bits_per_second: %f sender: %b", + (int64_t)sp->stream_id, (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); - } + iperf_printf(test, report_bw_format, sp->stream_id, mbuf, start_time, sender_time, ubuf, nbuf, report_sender); } } else { /* Sender summary, UDP. */ @@ -3193,23 +3295,19 @@ iperf_print_results(struct iperf_test *test) * instead. */ int 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)); + 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->stream_id, (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, 0, (sender_packet_count - sp->omitted_packet_count), (double) 0, report_sender); - } + iperf_printf(test, report_bw_udp_format, sp->stream_id, mbuf, start_time, sender_time, ubuf, nbuf, + 0.0, 0, (sender_packet_count - sp->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)); } @@ -3223,7 +3321,7 @@ iperf_print_results(struct iperf_test *test) 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'); + unit_snprintf(sbuf, UNIT_LEN, (double) sb.st_size, 'A', test->settings->unit_precision); 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 @@ -3231,32 +3329,30 @@ iperf_print_results(struct iperf_test *test) iperf_printf(test, report_diskfile, ubuf, sbuf, percent_sent, test->diskfile_name); } else { - unit_snprintf(ubuf, UNIT_LEN, (double) bytes_received, 'A'); + unit_snprintf(ubuf, UNIT_LEN, (double) bytes_received, 'A', test->settings->unit_precision); iperf_printf(test, report_diskfile, ubuf, sbuf, percent_received, test->diskfile_name); } } } - unit_snprintf(ubuf, UNIT_LEN, (double) bytes_received, 'A'); + unit_snprintf(ubuf, UNIT_LEN, (double) bytes_received, 'A', test->settings->unit_precision); 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); + unit_snprintf(nbuf, UNIT_LEN, bandwidth, test->settings->unit_format, test->settings->unit_precision); if (test->protocol->id == Ptcp || test->protocol->id == Psctp) { /* Receiver summary, TCP and SCTP */ if (test->json_output) - cJSON_AddItemToObject(json_summary_stream, "receiver", iperf_json_printf("socket: %d start: %f end: %f seconds: %f bytes: %d bits_per_second: %f sender: %b", (int64_t) sp->socket, (double) start_time, (double) receiver_time, (double) end_time, (int64_t) bytes_received, bandwidth * 8, stream_must_be_sender)); + cJSON_AddItemToObject(json_summary_stream, "receiver", + iperf_json_printf("id: %d socket: %d start: %f end: %f seconds: %f bytes: %d bits_per_second: %f sender: %b", + (int64_t)sp->stream_id, (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); - } + iperf_printf(test, report_bw_format, sp->stream_id, mbuf, start_time, receiver_time, ubuf, nbuf, report_receiver); } else { /* @@ -3272,13 +3368,9 @@ iperf_print_results(struct iperf_test *test) lost_percent = 0.0; } - 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_udp_format, sp->socket, mbuf, start_time, receiver_time, ubuf, nbuf, sp->jitter * 1000.0, (sp->cnt_error - sp->omitted_cnt_error), (receiver_packet_count - sp->omitted_packet_count), lost_percent, report_receiver); - } + iperf_printf(test, report_bw_udp_format, sp->stream_id, mbuf, start_time, receiver_time, ubuf, nbuf, + sp->jitter * 1000.0, (sp->cnt_error - sp->omitted_cnt_error), + (receiver_packet_count - sp->omitted_packet_count), lost_percent, report_receiver); } } } @@ -3286,7 +3378,7 @@ iperf_print_results(struct iperf_test *test) } if (test->num_streams > 1 || test->json_output) { - unit_snprintf(ubuf, UNIT_LEN, (double) total_sent, 'A'); + unit_snprintf(ubuf, UNIT_LEN, (double) total_sent, 'A', test->settings->unit_precision); /* If no tests were run, arbitrarily set bandwidth to 0. */ if (sender_time > 0.0) { bandwidth = (double) total_sent / (double) sender_time; @@ -3294,34 +3386,23 @@ iperf_print_results(struct iperf_test *test) else { bandwidth = 0.0; } - unit_snprintf(nbuf, UNIT_LEN, bandwidth, test->settings->unit_format); + unit_snprintf(nbuf, UNIT_LEN, bandwidth, test->settings->unit_format, test->settings->unit_precision); 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", 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); - } + 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", 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); - } + 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'); + unit_snprintf(ubuf, UNIT_LEN, (double) total_received, 'A', test->settings->unit_precision); /* If no tests were run, set received bandwidth to 0 */ if (receiver_time > 0.0) { bandwidth = (double) total_received / (double) receiver_time; @@ -3329,17 +3410,11 @@ iperf_print_results(struct iperf_test *test) else { bandwidth = 0.0; } - unit_snprintf(nbuf, UNIT_LEN, bandwidth, test->settings->unit_format); + unit_snprintf(nbuf, UNIT_LEN, bandwidth, test->settings->unit_format, test->settings->unit_precision); if (test->json_output) cJSON_AddItemToObject(test->json_end, "sum_received", 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); - } + 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; @@ -3359,12 +3434,12 @@ iperf_print_results(struct iperf_test *test) * server. Output whatever we have. */ if (! (test->role == 's' && !stream_must_be_sender) ) { - unit_snprintf(ubuf, UNIT_LEN, (double) total_sent, 'A'); + unit_snprintf(ubuf, UNIT_LEN, (double) total_sent, 'A', test->settings->unit_precision); iperf_printf(test, report_sum_bw_udp_format, mbuf, start_time, sender_time, ubuf, nbuf, 0.0, 0, sender_total_packets, 0.0, "sender"); } if (! (test->role == 's' && stream_must_be_sender) ) { - unit_snprintf(ubuf, UNIT_LEN, (double) total_received, 'A'); + unit_snprintf(ubuf, UNIT_LEN, (double) total_received, 'A', test->settings->unit_precision); /* Compute received bandwidth. */ if (end_time > 0.0) { bandwidth = (double) total_received / (double) receiver_time; @@ -3372,7 +3447,7 @@ iperf_print_results(struct iperf_test *test) else { bandwidth = 0.0; } - unit_snprintf(nbuf, UNIT_LEN, bandwidth, test->settings->unit_format); + unit_snprintf(nbuf, UNIT_LEN, bandwidth, test->settings->unit_format, test->settings->unit_precision); iperf_printf(test, report_sum_bw_udp_format, mbuf, start_time, receiver_time, ubuf, nbuf, avg_jitter * 1000.0, lost_packets, receiver_total_packets, lost_percent, "receiver"); } } @@ -3430,7 +3505,9 @@ iperf_print_results(struct iperf_test *test) /* 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) { - iperf_printf(test, "\nServer JSON output:\n%s\n", cJSON_Print(test->json_server_output)); + char* jp = cJSON_Print(test->json_server_output); + iperf_printf(test, "\nServer JSON output:\n%s\n", jp); + free(jp); cJSON_Delete(test->json_server_output); test->json_server_output = NULL; } @@ -3539,14 +3616,14 @@ print_interval_results(struct iperf_test *test, struct iperf_stream *sp, cJSON * } } - unit_snprintf(ubuf, UNIT_LEN, (double) (irp->bytes_transferred), 'A'); + unit_snprintf(ubuf, UNIT_LEN, (double) (irp->bytes_transferred), 'A', test->settings->unit_precision); 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); + unit_snprintf(nbuf, UNIT_LEN, bandwidth, test->settings->unit_format, test->settings->unit_precision); iperf_time_diff(&sp->result->start_time, &irp->interval_start_time, &temp_time); st = iperf_time_in_secs(&temp_time); @@ -3557,25 +3634,35 @@ print_interval_results(struct iperf_test *test, struct iperf_stream *sp, cJSON * 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 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->rtt, (int64_t) irp->rttvar, (int64_t) irp->pmtu, irp->omitted, sp->sender)); + cJSON_AddItemToArray(json_interval_streams, + iperf_json_printf("id: %d socket: %d start: %f end: %f seconds: %f bytes: %d bits_per_second: %f retransmits: %d snd_cwnd: %d rtt: %d rttvar: %d pmtu: %d omitted: %b sender: %b", + (int64_t)sp->stream_id, (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->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:""); + unit_snprintf(cbuf, UNIT_LEN, irp->snd_cwnd, 'A', test->settings->unit_precision); + iperf_printf(test, report_bw_retrans_cwnd_format, sp->stream_id, 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)); + cJSON_AddItemToArray(json_interval_streams, + iperf_json_printf("id: %d socket: %d start: %f end: %f seconds: %f bytes: %d bits_per_second: %f omitted: %b sender: %b", + (int64_t)sp->stream_id, (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:""); + iperf_printf(test, report_bw_format, sp->stream_id, 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)); + cJSON_AddItemToArray(json_interval_streams, iperf_json_printf("id: %d socket: %d start: %f end: %f seconds: %f bytes: %d bits_per_second: %f packets: %d omitted: %b sender: %b", (int64_t)sp->stream_id, (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:""); + iperf_printf(test, report_bw_udp_sender_format, sp->stream_id, 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; @@ -3584,9 +3671,9 @@ print_interval_results(struct iperf_test *test, struct iperf_stream *sp, cJSON * 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)); + cJSON_AddItemToArray(json_interval_streams, iperf_json_printf("id: %d 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->stream_id, (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:""); + iperf_printf(test, report_bw_udp_format, sp->stream_id, mbuf, st, et, ubuf, nbuf, irp->jitter * 1000.0, irp->interval_cnt_error, irp->interval_packet_count, lost_percent, irp->omitted?report_omitted:""); } } @@ -3601,10 +3688,11 @@ 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); + munmap(sp->buffer, sp->test->settings->buflen); close(sp->buffer_fd); - if (sp->diskfile_fd >= 0) + 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); @@ -3627,7 +3715,7 @@ iperf_new_stream(struct iperf_test *test, int s, int sender) snprintf(template, sizeof(template) / sizeof(char), "%s", test->tmp_template); } else { //find the system temporary dir *unix, windows, cygwin support - char* tempdir = getenv("TMPDIR"); + const char* tempdir = getenv("TMPDIR"); if (tempdir == 0){ tempdir = getenv("TEMP"); } @@ -3635,13 +3723,22 @@ iperf_new_stream(struct iperf_test *test, int s, int sender) tempdir = getenv("TMP"); } if (tempdir == 0){ +#ifndef __WIN32__ tempdir = "/tmp"; +#else + tempdir = ""; // CWD +#endif } +#ifndef __WIN32__ snprintf(template, sizeof(template) / sizeof(char), "%s/iperf3.XXXXXX", tempdir); +#else + snprintf(template, sizeof(template) / sizeof(char), "%s\\iperf3.XXXXXX", tempdir); +#endif } sp = (struct iperf_stream *) malloc(sizeof(struct iperf_stream)); if (!sp) { + iperf_err(test, "Failed to malloc iperf-stream.\n"); i_errno = IECREATESTREAM; return NULL; } @@ -3654,6 +3751,7 @@ iperf_new_stream(struct iperf_test *test, int s, int sender) sp->result = (struct iperf_stream_result *) malloc(sizeof(struct iperf_stream_result)); if (!sp->result) { free(sp); + iperf_err(test, "Failed to malloc sp->result."); i_errno = IECREATESTREAM; return NULL; } @@ -3662,27 +3760,38 @@ iperf_new_stream(struct iperf_test *test, int s, int sender) TAILQ_INIT(&sp->result->interval_results); /* Create and randomize the buffer */ + errno = 0; sp->buffer_fd = mkstemp(template); if (sp->buffer_fd == -1) { + iperf_err(test, "Failed to mkstemp %s (%s)", template, STRERROR); i_errno = IECREATESTREAM; free(sp->result); free(sp); return NULL; } + + // Windows will not allow one to unlink an open file, unfortunately. +#ifndef __WIN32__ + errno = 0; if (unlink(template) < 0) { + iperf_err(test, "Failed to unlink temp file: %s (%s)", template, STRERROR); i_errno = IECREATESTREAM; free(sp->result); free(sp); return NULL; } - if (ftruncate(sp->buffer_fd, test->settings->blksize) < 0) { +#endif + + if (ftruncate(sp->buffer_fd, test->settings->buflen) < 0) { + iperf_err(test, "Failed to truncate, fd: %d buflen: %d", sp->buffer_fd, test->settings->buflen); i_errno = IECREATESTREAM; free(sp->result); free(sp); return NULL; } - sp->buffer = (char *) mmap(NULL, test->settings->blksize, PROT_READ|PROT_WRITE, MAP_PRIVATE, sp->buffer_fd, 0); + sp->buffer = (char *) mmap(NULL, test->settings->buflen, PROT_READ|PROT_WRITE, MAP_PRIVATE, sp->buffer_fd, 0); if (sp->buffer == MAP_FAILED) { + iperf_err(test, "Failed to mmap."); i_errno = IECREATESTREAM; free(sp->result); free(sp); @@ -3699,7 +3808,7 @@ iperf_new_stream(struct iperf_test *test, int s, int sender) 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); + munmap(sp->buffer, sp->test->settings->buflen); free(sp->result); free(sp); return NULL; @@ -3713,13 +3822,13 @@ iperf_new_stream(struct iperf_test *test, int s, int sender) /* Initialize stream */ if (test->repeating_payload) - fill_with_repeating_pattern(sp->buffer, test->settings->blksize); + fill_with_repeating_pattern(sp->buffer, test->settings->buflen); else - ret = readentropy(sp->buffer, test->settings->blksize); + ret = readentropy(sp->buffer, test->settings->buflen); if ((ret < 0) || (iperf_init_stream(sp, test) < 0)) { close(sp->buffer_fd); - munmap(sp->buffer, sp->test->settings->blksize); + munmap(sp->buffer, sp->test->settings->buflen); free(sp->result); free(sp); return NULL; @@ -3749,9 +3858,10 @@ iperf_init_stream(struct iperf_stream *sp, struct iperf_test *test) /* Set IP TOS */ if ((opt = test->settings->tos)) { +#ifndef __WIN32__ /* windows does not support setting ToS */ if (getsockdomain(sp->socket) == AF_INET6) { #ifdef IPV6_TCLASS - if (setsockopt(sp->socket, IPPROTO_IPV6, IPV6_TCLASS, &opt, sizeof(opt)) < 0) { + if (setsockopt(sp->socket, IPPROTO_IPV6, IPV6_TCLASS, (const char*)&opt, sizeof(opt)) < 0) { i_errno = IESETCOS; return -1; } @@ -3760,11 +3870,15 @@ iperf_init_stream(struct iperf_stream *sp, struct iperf_test *test) return -1; #endif } else { - if (setsockopt(sp->socket, IPPROTO_IP, IP_TOS, &opt, sizeof(opt)) < 0) { + if (setsockopt(sp->socket, IPPROTO_IP, IP_TOS, (const char*)&opt, sizeof(opt)) < 0) { i_errno = IESETTOS; return -1; } } +#else + iperf_err(test, "WARNING: ToS: 0x%x requested, but windows does not support setting ToS. Ignoring.\n", + test->settings->tos); +#endif } return 0; @@ -3779,7 +3893,7 @@ iperf_add_stream(struct iperf_test *test, struct iperf_stream *sp) if (SLIST_EMPTY(&test->streams)) { SLIST_INSERT_HEAD(&test->streams, sp, streams); - sp->id = 1; + sp->stream_id = 1; } else { // for (n = test->streams, i = 2; n->next; n = n->next, ++i); i = 2; @@ -3788,7 +3902,7 @@ iperf_add_stream(struct iperf_test *test, struct iperf_stream *sp) ++i; } SLIST_INSERT_AFTER(prev, sp, streams); - sp->id = i; + sp->stream_id = i; } } @@ -3853,7 +3967,9 @@ diskfile_recv(struct iperf_stream *sp) r = sp->rcv2(sp); if (r > 0) { (void) write(sp->diskfile_fd, sp->buffer, r); +#ifndef __WIN32__ (void) fsync(sp->diskfile_fd); +#endif } return r; } @@ -3864,7 +3980,9 @@ iperf_catch_sigend(void (*handler)(int)) { signal(SIGINT, handler); signal(SIGTERM, handler); +#ifndef __WIN32__ signal(SIGHUP, handler); +#endif } /** @@ -3886,20 +4004,24 @@ iperf_got_sigend(struct iperf_test *test) test->done = 1; cpu_util(test->cpu_util); test->stats_callback(test); - test->state = DISPLAY_RESULTS; /* change local state only */ + iperf_set_state(test, DISPLAY_RESULTS, __FUNCTION__); /* 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); + iperf_set_state(test, (test->role == 'c') ? CLIENT_TERMINATE : SERVER_TERMINATE, "got-sig-end"); + waitWrite(test->ctrl_sck, (char*) &test->state, sizeof(signed char), Ptcp, test, ctrl_wait_ms); } i_errno = (test->role == 'c') ? IECLIENTTERM : IESERVERTERM; iperf_errexit(test, "interrupt - %s", iperf_strerror(i_errno)); } +#ifdef __WIN32__ + +#endif + /* Try to write a PID file if requested, return -1 on an error. */ int iperf_create_pidfile(struct iperf_test *test) @@ -3918,6 +4040,7 @@ iperf_create_pidfile(struct iperf_test *test) pid = atoi(buf); if (pid > 0) { +#ifndef __WIN32__ /* don't know how to kill on windows */ /* See if the process exists. */ if (kill(pid, 0) == 0) { /* @@ -3929,6 +4052,7 @@ iperf_create_pidfile(struct iperf_test *test) test->pidfile = NULL; iperf_errexit(test, "Another instance of iperf3 appears to be running"); } +#endif } } } @@ -4119,11 +4243,13 @@ iperf_printf(struct iperf_test *test, const char* format, ...) * easily exceed the size of the line buffer, but which don't need * to be buffered up anyway. */ + int rv = -1; if (test->role == 'c') { if (test->title) fprintf(test->outfile, "%s: ", test->title); va_start(argp, format); r = vfprintf(test->outfile, format, argp); + rv = r; va_end(argp); } else if (test->role == 's') { @@ -4131,7 +4257,7 @@ iperf_printf(struct iperf_test *test, const char* format, ...) va_start(argp, format); r = vsnprintf(linebuffer, sizeof(linebuffer), format, argp); va_end(argp); - fprintf(test->outfile, "%s", linebuffer); + rv = 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)); @@ -4139,11 +4265,20 @@ iperf_printf(struct iperf_test *test, const char* format, ...) TAILQ_INSERT_TAIL(&(test->server_output_list), l, textlineentries); } } + + if (rv < 0) { + /* If printing output fails, go ahead and exit, probably broken pipe */ + iperf_errexit(test, "print to outfile failed: %s\n", strerror(errno)); + } return r; } int iflush(struct iperf_test *test) { - return fflush(test->outfile); + int rv = fflush(test->outfile); + if (rv < 0) { + iperf_errexit(test, "fflush on outfile failed: %s\n", strerror(errno)); + } + return rv; } diff --git a/src/iperf_api.h b/src/iperf_api.h index 7643b17ce..3690afe23 100755 --- a/src/iperf_api.h +++ b/src/iperf_api.h @@ -27,7 +27,9 @@ #ifndef __IPERF_API_H #define __IPERF_API_H +#ifndef __WIN32__ #include +#endif #include #include #include @@ -38,6 +40,7 @@ extern "C" { /* open extern "C" */ #endif +#include "iperf.h" struct iperf_test; struct iperf_stream_result; @@ -73,16 +76,18 @@ struct iperf_time; #define OPT_REPEATING_PAYLOAD 18 #define OPT_EXTRA_DATA 19 #define OPT_BIDIRECTIONAL 20 +#define OPT_BIND_DEV 21 /* states */ +#define TEST_INIT 0 #define TEST_START 1 #define TEST_RUNNING 2 -#define RESULT_REQUEST 3 /* not used */ +//#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 STREAM_BEGIN 5 /* not used */ +#define STREAM_RUNNING 6 +//#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 @@ -94,6 +99,12 @@ struct iperf_time; #define ACCESS_DENIED (-1) #define SERVER_ERROR (-2) +const char* iperf_get_state_str(int s); +void _fd_set(int fd, fd_set* fdset, struct iperf_test *test, const char* file, int line); +void _fd_clr(int fd, fd_set* fdset, struct iperf_test *test, const char* file, int line); +#define IFD_SET(a, b, c) _fd_set(a, b, c, __FILE__, __LINE__) +#define IFD_CLR(a, b, c) _fd_clr(a, b, c, __FILE__, __LINE__) + /* 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 ); @@ -257,6 +268,7 @@ 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); +void iperf_set_state(struct iperf_test *test, signed char state, const char* dbg); int iperf_set_send_state(struct iperf_test *test, signed char state); void iperf_check_throttle(struct iperf_stream *sp, struct iperf_time *nowP); int iperf_send(struct iperf_test *, fd_set *) /* __attribute__((hot)) */; @@ -265,7 +277,6 @@ 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(char *); int iperf_exchange_results(struct iperf_test *); int iperf_init_test(struct iperf_test *); int iperf_create_send_timers(struct iperf_test *); @@ -292,6 +303,7 @@ int iperf_handle_message_client(struct iperf_test *); int iperf_client_end(struct iperf_test *); /* Server routines. */ +void cleanup_server(struct iperf_test *test); int iperf_run_server(struct iperf_test *); int iperf_server_listen(struct iperf_test *); int iperf_accept(struct iperf_test *); @@ -402,6 +414,7 @@ enum { /* Timer errors */ IENEWTIMER = 300, // Unable to create new timer (check perror) IEUPDATETIMER = 301, // Unable to update timer (check perror) + IEBINDDEV = 302, // Unable to bind-to-device (check perror, maybe permissions?) }; diff --git a/src/iperf_auth.c b/src/iperf_auth.c index 0a5f53323..23fc23f7c 100644 --- a/src/iperf_auth.c +++ b/src/iperf_auth.c @@ -25,16 +25,18 @@ * for complete information. */ -#include "iperf_config.h" +#include "iperf_auth.h" +#include #include #include -#include #include /* FreeBSD needs _WITH_GETLINE to enable the getline() declaration */ #define _WITH_GETLINE #include +#ifndef __WIN32__ #include +#endif #if defined(HAVE_SSL) @@ -116,7 +118,12 @@ int Base64Encode(const unsigned char* buffer, const size_t length, char** b64tex BIO_write(bio, buffer, length); BIO_flush(bio); BIO_get_mem_ptr(bio, &bufferPtr); +#ifndef __WIN32__ *b64text = strndup( (*bufferPtr).data, (*bufferPtr).length ); +#else + *b64text = malloc((*bufferPtr).length); + strncpy(*b64text, (*bufferPtr).data, (*bufferPtr).length); +#endif BIO_free_all(bio); return (0); //success @@ -275,7 +282,7 @@ int encode_auth_setting(const char *username, const char *password, EVP_PKEY *pu return (0); //success } -int decode_auth_setting(int enable_debug, char *authtoken, EVP_PKEY *private_key, char **username, char **password, time_t *ts){ +int decode_auth_setting(int enable_debug, const char *authtoken, EVP_PKEY *private_key, char **username, char **password, time_t *ts){ unsigned char *encrypted_b64 = NULL; size_t encrypted_len_b64; Base64Decode(authtoken, &encrypted_b64, &encrypted_len_b64); @@ -303,9 +310,10 @@ int decode_auth_setting(int enable_debug, char *authtoken, EVP_PKEY *private_key #endif //HAVE_SSL ssize_t iperf_getpass (char **lineptr, size_t *n, FILE *stream) { - struct termios old, new; ssize_t nread; +#ifndef __WIN32__ + struct termios old, new; /* Turn echoing off and fail if we can't. */ if (tcgetattr (fileno (stream), &old) != 0) return -1; @@ -313,13 +321,16 @@ ssize_t iperf_getpass (char **lineptr, size_t *n, FILE *stream) { new.c_lflag &= ~ECHO; if (tcsetattr (fileno (stream), TCSAFLUSH, &new) != 0) return -1; +#endif /* Read the password. */ printf("Password: "); nread = getline (lineptr, n, stream); +#ifndef __WIN32__ /* Restore terminal. */ (void) tcsetattr (fileno (stream), TCSAFLUSH, &old); +#endif //strip the \n or \r\n chars char *buf = *lineptr; diff --git a/src/iperf_auth.h b/src/iperf_auth.h index 0dd6c4cdc..3f5ce64a9 100644 --- a/src/iperf_auth.h +++ b/src/iperf_auth.h @@ -25,10 +25,143 @@ * for complete information. */ +#include "iperf_config.h" + #include #include #include +#ifdef __WIN32__ + +typedef intptr_t ssize_t; + +ssize_t getline(char **lineptr, size_t *n, FILE *stream); + +/* Replacement termios.h for MinGW32. + Copyright (C) 2008 CodeSourcery, Inc. + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +typedef unsigned char cc_t; +typedef unsigned int tcflag_t; + +#define NCCS 18 + +struct termios +{ + tcflag_t c_iflag; + tcflag_t c_oflag; + tcflag_t c_cflag; + tcflag_t c_lflag; + cc_t c_cc[NCCS]; +}; + +#define VEOF 0 +#define VEOL 1 +#define VERASE 2 +#define VINTR 3 +#define VKILL 4 +#define VMIN 5 +#define VQUIT 6 +#define VSTART 7 +#define VSTOP 8 +#define VSUSP 9 +#define VTIME 10 +#define VEOL2 11 +#define VWERASE 12 +#define VREPRINT 13 +#define VLNEXT 15 +#define VDISCARD 16 + +#define _POSIX_VDISABLE '\0' + +#define BRKINT 0x1 +#define ICRNL 0x2 +#define IGNBRK 0x4 +#define IGNCR 0x8 +#define IGNPAR 0x10 +#define INLCR 0x20 +#define INPCK 0x40 +#define ISTRIP 0x80 +#define IXANY 0x100 +#define IXOFF 0x200 +#define IXON 0x400 +#define PARMRK 0x800 + +#define OPOST 0x1 +#define ONLCR 0x2 +#define OCRNL 0x4 +#define ONOCR 0x8 +#define ONLRET 0x10 +#define OFILL 0x20 + +#define CSIZE 0x3 +#define CS5 0x0 +#define CS6 0x1 +#define CS7 0x2 +#define CS8 0x3 +#define CSTOPB 0x4 +#define CREAD 0x8 +#define PARENB 0x10 +#define PARODD 0x20 +#define HUPCL 0x40 +#define CLOCAL 0x80 + +#define ECHO 0x1 +#define ECHOE 0x2 +#define ECHOK 0x4 +#define ECHONL 0x8 +#define ICANON 0x10 +#define IEXTEN 0x20 +#define ISIG 0x40 +#define NOFLSH 0x80 +#define TOSTOP 0x100 +#define FLUSHO 0x200 + +#define TCSANOW 0 +#define TCSADRAIN 1 +#define TCSAFLUSH 2 + +#define TCIOFF 0 +#define TCION 1 +#define TCOOFF 2 +#define TCOON 3 + +int tcgetattr (int fd, struct termios *buf); +int tcsetattr (int fd, int actions, const struct termios *buf); +int tcdrain (int fd); +int tcflow (int fd, int action); + +/* We want to intercept TIOCGWINSZ, but not FIONREAD. No need to forward + TIOCSWINSZ; readline only uses it to suspend if in the background. + Readline doesn't make any other ioctl calls on mingw. */ + +#include + +struct winsize +{ + unsigned short ws_row; + unsigned short ws_col; +}; + +int mingw_getwinsize (struct winsize *window_size); +#define TIOCGWINSZ 0x42424240 +#define TIOCSWINSZ 0x42424241 +#define ioctl(fd, op, arg) \ + (((op) == TIOCGWINSZ) ? mingw_getwinsize (arg) \ + : ((op) == TIOCSWINSZ) ? -1 \ + : ioctlsocket (fd, op, arg)) + +#endif /* win32 */ + 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); diff --git a/src/iperf_client_api.c b/src/iperf_client_api.c index 20ea6fda5..18b9a3377 100644 --- a/src/iperf_client_api.c +++ b/src/iperf_client_api.c @@ -32,10 +32,12 @@ #include #include #include +#ifndef __WIN32__ #include #include #include #include +#endif #include "iperf.h" #include "iperf_api.h" @@ -60,6 +62,11 @@ iperf_create_streams(struct iperf_test *test, int sender) #endif /* HAVE_TCP_CONGESTION */ struct iperf_stream *sp; + if (test->debug) { + fprintf(stderr, "iperf-create-streams, num-streams: %d\n", + test->num_streams); + } + int orig_bind_port = test->bind_port; for (i = 0; i < test->num_streams; ++i) { @@ -74,7 +81,7 @@ iperf_create_streams(struct iperf_test *test, int sender) if (test->congestion) { if (setsockopt(s, IPPROTO_TCP, TCP_CONGESTION, test->congestion, strlen(test->congestion)) < 0) { saved_errno = errno; - close(s); + iclosesocket(s, test); errno = saved_errno; i_errno = IESETCONGESTION; return -1; @@ -85,7 +92,7 @@ iperf_create_streams(struct iperf_test *test, int sender) char ca[TCP_CA_NAME_MAX + 1]; if (getsockopt(s, IPPROTO_TCP, TCP_CONGESTION, ca, &len) < 0) { saved_errno = errno; - close(s); + iclosesocket(s, test); errno = saved_errno; i_errno = IESETCONGESTION; return -1; @@ -99,14 +106,14 @@ iperf_create_streams(struct iperf_test *test, int sender) #endif /* HAVE_TCP_CONGESTION */ if (sender) - FD_SET(s, &test->write_set); - else - FD_SET(s, &test->read_set); - if (s > test->max_fd) test->max_fd = s; + IFD_SET(s, &test->write_set, test); + IFD_SET(s, &test->read_set, test); sp = iperf_new_stream(test, s, sender); - if (!sp) + if (!sp) { + iclosesocket(s, test); return -1; + } /* Perform the new stream callback */ if (test->on_new_stream) @@ -121,6 +128,8 @@ test_timer_proc(TimerClientData client_data, struct iperf_time *nowP) { struct iperf_test *test = client_data.p; + if (test->debug) + iperf_err(test, "test-timer-proc, setting done."); test->timer = NULL; test->done = 1; } @@ -161,7 +170,7 @@ create_client_timers(struct iperf_test * 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); + 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; @@ -232,9 +241,9 @@ iperf_handle_message_client(struct iperf_test *test) { int rval; int32_t err; + signed char s; - /*!!! Why is this read() and not Nread()? */ - if ((rval = read(test->ctrl_sck, (char*) &test->state, sizeof(signed char))) <= 0) { + if ((rval = waitRead(test->ctrl_sck, (char*) &s, sizeof(s), Ptcp, test, ctrl_wait_ms)) != sizeof(s)) { if (rval == 0) { i_errno = IECTRLCLOSE; return -1; @@ -243,6 +252,9 @@ iperf_handle_message_client(struct iperf_test *test) return -1; } } + else { + iperf_set_state(test, s, __FUNCTION__); + } switch (test->state) { case PARAM_EXCHANGE: @@ -295,20 +307,20 @@ iperf_handle_message_client(struct iperf_test *test) */ signed char oldstate = test->state; cpu_util(test->cpu_util); - test->state = DISPLAY_RESULTS; + iperf_set_state(test, DISPLAY_RESULTS, __FUNCTION__); test->reporter_callback(test); - test->state = oldstate; + iperf_set_state(test, oldstate, __FUNCTION__); return -1; case ACCESS_DENIED: i_errno = IEACCESSDENIED; return -1; case SERVER_ERROR: - if (Nread(test->ctrl_sck, (char*) &err, sizeof(err), Ptcp) < 0) { + if (waitRead(test->ctrl_sck, (char*) &err, sizeof(err), Ptcp, test, ctrl_wait_ms) != sizeof(err)) { i_errno = IECTRLREAD; return -1; } i_errno = ntohl(err); - if (Nread(test->ctrl_sck, (char*) &err, sizeof(err), Ptcp) < 0) { + if (waitRead(test->ctrl_sck, (char*) &err, sizeof(err), Ptcp, test, ctrl_wait_ms) != sizeof(err)) { i_errno = IECTRLREAD; return -1; } @@ -336,25 +348,32 @@ iperf_connect(struct iperf_test *test) /* 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, 0, test->server_hostname, test->server_port, test->settings->connect_timeout); + 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, test); if (test->ctrl_sck < 0) { i_errno = IECONNECT; return -1; } - if (Nwrite(test->ctrl_sck, test->cookie, COOKIE_SIZE, Ptcp) < 0) { + setnonblocking(test->ctrl_sck, 1); + + if (waitWrite(test->ctrl_sck, test->cookie, COOKIE_SIZE, Ptcp, test, ctrl_wait_ms) != COOKIE_SIZE) { 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; + IFD_SET(test->ctrl_sck, &test->read_set, test); int opt; socklen_t len; len = sizeof(opt); - if (getsockopt(test->ctrl_sck, IPPROTO_TCP, TCP_MAXSEG, &opt, &len) < 0) { +#ifdef __WIN32__ + if (1) +#else + if (getsockopt(test->ctrl_sck, IPPROTO_TCP, TCP_MAXSEG, &opt, &len) < 0) +#endif + { test->ctrl_sck_mss = 0; } else { @@ -362,10 +381,7 @@ iperf_connect(struct iperf_test *test) test->ctrl_sck_mss = opt; } else { - char str[128]; - snprintf(str, sizeof(str), - "Ignoring nonsense TCP MSS %d", opt); - warning(str); + iperf_err(test, "Ignoring nonsense TCP MSS %d", opt); test->ctrl_sck_mss = 0; } @@ -409,10 +425,8 @@ iperf_connect(struct iperf_test *test) */ if (test->ctrl_sck_mss > 0 && test->settings->blksize > test->ctrl_sck_mss) { - char str[128]; - 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); + iperf_err(test, "UDP block size %d exceeds TCP MSS %d, may result in fragmentation / drops", + test->settings->blksize, test->ctrl_sck_mss); } } @@ -427,7 +441,8 @@ iperf_client_end(struct iperf_test *test) /* Close all stream sockets */ SLIST_FOREACH(sp, &test->streams, streams) { - close(sp->socket); + iclosesocket(sp->socket, test); + sp->socket = -1; } /* show final summary */ @@ -437,8 +452,7 @@ iperf_client_end(struct iperf_test *test) return -1; /* Close control socket */ - if (test->ctrl_sck) - close(test->ctrl_sck); + iclosesocket(test->ctrl_sck, test); return 0; } @@ -485,6 +499,7 @@ iperf_run_client(struct iperf_test * test) startup = 1; while (test->state != IPERF_DONE) { + int ran_timers = 0; memcpy(&read_set, &test->read_set, sizeof(fd_set)); memcpy(&write_set, &test->write_set, sizeof(fd_set)); iperf_time_now(&now); @@ -499,7 +514,7 @@ iperf_run_client(struct iperf_test * test) if (iperf_handle_message_client(test) < 0) { return -1; } - FD_CLR(test->ctrl_sck, &read_set); + IFD_CLR(test->ctrl_sck, &read_set, test); } } @@ -523,7 +538,7 @@ iperf_run_client(struct iperf_test * test) if (iperf_send(test, &write_set) < 0) return -1; if (iperf_recv(test, &read_set) < 0) - return -1; + test->done = 1; // peer could have just closed the connection since test was done. } else if (test->mode == SENDER) { // Regular mode. Client sends. if (iperf_send(test, &write_set) < 0) @@ -531,28 +546,22 @@ iperf_run_client(struct iperf_test * test) } else { // Reverse mode. Client receives. if (iperf_recv(test, &read_set) < 0) - return -1; + test->done = 1; // peer could have just closed the connection since test was done. } /* Run the timers. */ iperf_time_now(&now); tmr_run(&now); + ran_timers = 1; /* Is the test done yet? */ - if ((!test->omitting) && - ((test->duration != 0 && test->done) || - (test->settings->bytes != 0 && test->bytes_sent >= test->settings->bytes) || - (test->settings->blocks != 0 && test->blocks_sent >= test->settings->blocks))) { - - // Unset non-blocking for non-UDP tests - if (test->protocol->id != Pudp) { - SLIST_FOREACH(sp, &test->streams, streams) { - setnonblocking(sp->socket, 0); - } - } + if (test->done || + (test->settings->bytes != 0 && test->bytes_sent >= test->settings->bytes) || + (test->settings->blocks != 0 && test->blocks_sent >= test->settings->blocks)) { /* Yes, done! Send TEST_END. */ + //iperf_err(test, "test is done"); test->done = 1; cpu_util(test->cpu_util); test->stats_callback(test); @@ -566,10 +575,26 @@ iperf_run_client(struct iperf_test * test) // and gets blocked, so it can't receive state changes // from the client side. else if (test->mode == RECEIVER && test->state == TEST_END) { - if (iperf_recv(test, &read_set) < 0) - return -1; + iperf_recv(test, &read_set); + + /* Recv sockets have been drained, wait a bit to see if we can exchange results */ + if (test->done_at_ms == 0) { + test->done_at_ms = getCurMs(); + } + else { + if (test->done_at_ms + 3000 < getCurMs()) { // 3 seconds to exchange results + iperf_err(test, "Did not finish exchanging results within 3 seconds."); + break; + } + } } - } + + if (!ran_timers) { + /* Run the timers. */ + iperf_time_now(&now); + tmr_run(&now); + } + }/* while test state is not done */ if (test->json_output) { if (iperf_json_finish(test) < 0) diff --git a/src/iperf_error.c b/src/iperf_error.c index fd3cccca6..62ee265ac 100644 --- a/src/iperf_error.c +++ b/src/iperf_error.c @@ -26,7 +26,6 @@ */ #include #include -#include #include #include #include @@ -42,17 +41,36 @@ iperf_err(struct iperf_test *test, const char *format, ...) va_list argp; char str[1000]; + /* For reasons that I don't understand, when I run iperf with debugging on windows, + * (inside power-shell on win 7 specifically) + * Then it appears it blocks on writing to stderr fairly often, until I hit enter in + * the shell. + * I do not see the problem writing to stdout. So, switch err messages to stdout for + * windows. --Ben + */ +#ifdef __WIN32__ + #define __OFILE stdout +#else + #define __OFILE stderr +#endif + va_start(argp, format); vsnprintf(str, sizeof(str), format, argp); - if (test != NULL && test->json_output && test->json_top != NULL) + 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) { - fprintf(test->outfile, "iperf3: %s\n", str); + } + else { + if (test && test->outfile && test->outfile != __OFILE) { + fprintf(test->outfile, "%llu %s %s iperf3: %s\n", + (unsigned long long)getCurMs(), test->protocol ? test->protocol->name : "NULL-PROTO", + iperf_get_state_str(test->state), str); } else { - fprintf(stderr, "iperf3: %s\n", str); + fprintf(__OFILE, "%llu %s %s iperf3: %s\n", + (unsigned long long)getCurMs(), test->protocol ? test->protocol->name : "NULL-PROTO", + iperf_get_state_str(test->state), str); } + } va_end(argp); } @@ -93,306 +111,231 @@ iperf_strerror(int int_errno) len = sizeof(errstr); memset(errstr, 0, len); + /* Put text on same line as error to make grepping easier */ switch (int_errno) { - case IENONE: - snprintf(errstr, len, "no error"); + case IENONE: snprintf(errstr, len, "no error"); break; - case IESERVCLIENT: - snprintf(errstr, len, "cannot be both server and client"); + 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)"); + 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"); + 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"); + 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); + 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); + 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); + 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); + 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); + 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"); + 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); + 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)"); + case IEBADTOS: snprintf(errstr, len, "bad TOS value (must be between 0 and 255 inclusive)"); break; - case IESETCLIENTAUTH: - snprintf(errstr, len, "you must specify username (max 20 chars), password (max 20 chars) and a path to a valid public rsa client to be used"); + case IESETCLIENTAUTH: snprintf(errstr, len, "you must specify username (max 20 chars), password (max 20 chars) and a path to a valid public rsa client to be used"); break; - case IESETSERVERAUTH: - snprintf(errstr, len, "you must specify path to a valid private rsa server to be used and a user credential file"); + case IESETSERVERAUTH: snprintf(errstr, len, "you must specify path to a valid private rsa server to be used and a user credential file"); break; - case IEBADFORMAT: - snprintf(errstr, len, "bad format specifier (valid formats are in the set [kmgtKMGT])"); + 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"); + 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); + 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"); + case IENOSENDFILE: snprintf(errstr, len, "this OS does not support sendfile"); break; - case IEOMIT: - snprintf(errstr, len, "bogus value for --omit"); + 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"); + 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"); + 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); + 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"); + 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"); + case IELOGFILE: snprintf(errstr, len, "unable to open log file"); perr = 1; break; - case IENOSCTP: - snprintf(errstr, len, "no SCTP support available"); + case IENOSCTP: snprintf(errstr, len, "no SCTP support available"); break; - case IENEWTEST: - snprintf(errstr, len, "unable to create a new test"); + case IENEWTEST: snprintf(errstr, len, "unable to create a new test"); perr = 1; break; - case IEINITTEST: - snprintf(errstr, len, "test initialization failed"); + case IEINITTEST: snprintf(errstr, len, "test initialization failed"); perr = 1; break; - case IEAUTHTEST: - snprintf(errstr, len, "test authorization failed"); + case IEAUTHTEST: snprintf(errstr, len, "test authorization failed"); break; - case IELISTEN: - snprintf(errstr, len, "unable to start listener for connections"); + 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"); + case IECONNECT: snprintf(errstr, len, "unable to connect to server"); perr = 1; herr = 1; break; - case IEACCEPT: - snprintf(errstr, len, "unable to accept connection from client"); + 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"); + 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"); + 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"); + 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"); + 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"); + case IECTRLCLOSE: snprintf(errstr, len, "control socket has closed unexpectedly"); break; - case IEMESSAGE: - snprintf(errstr, len, "received an unknown control message"); + case IEMESSAGE: snprintf(errstr, len, "received an unknown control message"); break; - case IESENDMESSAGE: - snprintf(errstr, len, "unable to send control message"); + case IESENDMESSAGE: snprintf(errstr, len, "unable to send control message"); perr = 1; break; - case IERECVMESSAGE: - snprintf(errstr, len, "unable to receive control message"); + case IERECVMESSAGE: snprintf(errstr, len, "unable to receive control message"); perr = 1; break; - case IESENDPARAMS: - snprintf(errstr, len, "unable to send parameters to server"); + 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"); + case IERECVPARAMS: snprintf(errstr, len, "unable to receive parameters from client"); perr = 1; break; - case IEPACKAGERESULTS: - snprintf(errstr, len, "unable to package results"); + case IEPACKAGERESULTS: snprintf(errstr, len, "unable to package results"); perr = 1; break; - case IESENDRESULTS: - snprintf(errstr, len, "unable to send results"); + case IESENDRESULTS: snprintf(errstr, len, "unable to send results"); perr = 1; break; - case IERECVRESULTS: - snprintf(errstr, len, "unable to receive results"); + case IERECVRESULTS: snprintf(errstr, len, "unable to receive results"); perr = 1; break; - case IESELECT: - snprintf(errstr, len, "select failed"); + case IESELECT: snprintf(errstr, len, "select failed"); perr = 1; break; - case IECLIENTTERM: - snprintf(errstr, len, "the client has terminated"); + case IECLIENTTERM: snprintf(errstr, len, "the client has terminated"); break; - case IESERVERTERM: - snprintf(errstr, len, "the server has terminated"); + 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"); + 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"); + 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"); + 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"); + case IESETBUF: snprintf(errstr, len, "unable to set socket buffer size"); perr = 1; break; - case IESETTOS: - snprintf(errstr, len, "unable to set IP TOS"); + case IESETTOS: snprintf(errstr, len, "unable to set IP TOS"); perr = 1; break; - case IESETCOS: - snprintf(errstr, len, "unable to set IPv6 traffic class"); + 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"); + case IESETFLOW: snprintf(errstr, len, "unable to set IPv6 flow label"); break; - case IEREUSEADDR: - snprintf(errstr, len, "unable to reuse address on socket"); + 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"); + 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"); + case IESETWINDOWSIZE: snprintf(errstr, len, "unable to set socket window size"); perr = 1; break; - case IEPROTOCOL: - snprintf(errstr, len, "protocol does not exist"); + case IEPROTOCOL: snprintf(errstr, len, "protocol does not exist"); break; - case IEAFFINITY: - snprintf(errstr, len, "unable to set CPU affinity"); + case IEAFFINITY: snprintf(errstr, len, "unable to set CPU affinity"); perr = 1; break; - case IEDAEMON: - snprintf(errstr, len, "unable to become a daemon"); + case IEDAEMON: snprintf(errstr, len, "unable to become a daemon"); perr = 1; break; - case IECREATESTREAM: - snprintf(errstr, len, "unable to create a new stream"); + 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"); + case IEINITSTREAM: snprintf(errstr, len, "unable to initialize stream"); herr = 1; perr = 1; break; - case IESTREAMLISTEN: - snprintf(errstr, len, "unable to start stream listener"); + case IESTREAMLISTEN: snprintf(errstr, len, "unable to start stream listener"); herr = 1; perr = 1; break; - case IESTREAMCONNECT: - snprintf(errstr, len, "unable to connect stream"); + case IESTREAMCONNECT: snprintf(errstr, len, "unable to connect stream"); herr = 1; perr = 1; break; - case IESTREAMACCEPT: - snprintf(errstr, len, "unable to accept stream connection"); + case IESTREAMACCEPT: snprintf(errstr, len, "unable to accept stream connection"); perr = 1; break; - case IESTREAMWRITE: - snprintf(errstr, len, "unable to write to stream socket"); + 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"); + case IESTREAMREAD: snprintf(errstr, len, "unable to read from stream socket"); perr = 1; break; - case IESTREAMCLOSE: - snprintf(errstr, len, "stream socket has closed unexpectedly"); + case IESTREAMCLOSE: snprintf(errstr, len, "stream socket has closed unexpectedly"); break; - case IESTREAMID: - snprintf(errstr, len, "stream has an invalid id"); + case IESTREAMID: snprintf(errstr, len, "stream has an invalid id"); break; - case IENEWTIMER: - snprintf(errstr, len, "unable to create new timer"); + case IENEWTIMER: snprintf(errstr, len, "unable to create new timer"); perr = 1; break; - case IEUPDATETIMER: - snprintf(errstr, len, "unable to update timer"); + 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"); + 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"); + case IEPIDFILE: snprintf(errstr, len, "unable to write PID file"); perr = 1; break; - case IEV6ONLY: - snprintf(errstr, len, "Unable to set/reset IPV6_V6ONLY"); + 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"); + 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"); + 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"); + case IESETPACING: snprintf(errstr, len, "unable to set socket pacing"); perr = 1; break; - case IESETBUF2: - snprintf(errstr, len, "socket buffer size not set correctly"); + 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 IEREVERSEBIDIR: snprintf(errstr, len, "cannot be both reverse and bidirectional"); + break; } /* Append the result of strerror() or gai_strerror() if appropriate */ if (herr || perr) strncat(errstr, ": ", len - strlen(errstr) - 1); +#ifndef __WIN32__ if (errno && perr) strncat(errstr, strerror(errno), len - strlen(errstr) - 1); +#else + if (perr) + strncat(errstr, STRERROR, len - strlen(errstr) - 1); +#endif else if (herr && gerror) { strncat(errstr, gai_strerror(gerror), len - strlen(errstr) - 1); gerror = 0; diff --git a/src/iperf_locale.c b/src/iperf_locale.c index 0d8a7ecc5..5464f1cec 100644 --- a/src/iperf_locale.c +++ b/src/iperf_locale.c @@ -99,23 +99,25 @@ const char usage_longstr[] = "Usage: iperf3 [-s|-c host] [options]\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" + " -r, --precision [-1 - 30] decimal precision for format to report\n" " -i, --interval # seconds between periodic throughput reports\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 */ " -B, --bind bind to the interface associated with the address \n" + " --bind_dev bind to the network interface with SO_BINDTODEVICE\n" " -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" + " -I, --pidfile file write PID file\n" " -d, --debug emit debugging output\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" - " -I, --pidfile file write PID file\n" " -1, --one-off handle one client connection then exit\n" #if defined(HAVE_SSL) " --rsa-private-key-path path to the RSA private key used to decrypt\n" @@ -225,6 +227,9 @@ const char client_port[] = 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"; @@ -410,10 +415,6 @@ 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[] = diff --git a/src/iperf_locale.h b/src/iperf_locale.h index ad784a69a..1df6f638f 100644 --- a/src/iperf_locale.h +++ b/src/iperf_locale.h @@ -36,6 +36,7 @@ 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[] ; diff --git a/src/iperf_sctp.c b/src/iperf_sctp.c index 06e1e230a..0ff91f2ae 100644 --- a/src/iperf_sctp.c +++ b/src/iperf_sctp.c @@ -24,6 +24,8 @@ * This code is distributed under a BSD style license, see the LICENSE * file for complete information. */ +#ifndef __WIN32__ + #include "iperf_config.h" #include @@ -49,7 +51,6 @@ #include "net.h" - /* iperf_sctp_recv * * receives the data for SCTP @@ -60,7 +61,7 @@ iperf_sctp_recv(struct iperf_stream *sp) #if defined(HAVE_SCTP) int r; - r = Nread(sp->socket, sp->buffer, sp->settings->blksize, Psctp); + r = Nread(sp->socket, sp->buffer, sp->settings->blksize, Psctp, sp->test); if (r < 0) return r; @@ -92,7 +93,7 @@ iperf_sctp_send(struct iperf_stream *sp) #if defined(HAVE_SCTP) int r; - r = Nwrite(sp->socket, sp->buffer, sp->settings->blksize, Psctp); + r = Nwrite(sp->socket, sp->buffer, sp->settings->blksize, Psctp, sp->test); if (r < 0) return r; @@ -129,13 +130,13 @@ iperf_sctp_accept(struct iperf_test * test) return -1; } - if (Nread(s, cookie, COOKIE_SIZE, Psctp) < 0) { + if (Nread(s, cookie, COOKIE_SIZE, Psctp, test) < 0) { i_errno = IERECVCOOKIE; return -1; } if (strcmp(test->cookie, cookie) != 0) { - if (Nwrite(s, (char*) &rbuf, sizeof(rbuf), Psctp) < 0) { + if (Nwrite(s, (char*) &rbuf, sizeof(rbuf), Psctp, test) < 0) { i_errno = IESENDMESSAGE; return -1; } @@ -189,6 +190,21 @@ iperf_sctp_listen(struct iperf_test *test) return -1; } + if (test->bind_dev) { +#ifdef SO_BINDTODEVICE + if (setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE, + test->bind_dev, IFNAMSIZ) < 0) +#endif + { + 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)) { @@ -260,7 +276,7 @@ iperf_sctp_connect(struct iperf_test *test) #if defined(HAVE_SCTP) int s, opt, saved_errno; char portstr[6]; - struct addrinfo hints, *local_res, *server_res; + struct addrinfo hints, *local_res = NULL, *server_res = NULL; if (test->bind_address) { memset(&hints, 0, sizeof(hints)); @@ -285,13 +301,28 @@ iperf_sctp_connect(struct iperf_test *test) s = socket(server_res->ai_family, SOCK_STREAM, IPPROTO_SCTP); if (s < 0) { - if (test->bind_address) - freeaddrinfo(local_res); + freeaddrinfo(local_res); freeaddrinfo(server_res); i_errno = IESTREAMCONNECT; return -1; } + if (test->bind_dev) { +#ifdef SO_BINDTODEVICE + if (setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE, + test->bind_dev, IFNAMSIZ) < 0) +#endif + { + 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). @@ -449,7 +480,7 @@ iperf_sctp_connect(struct iperf_test *test) freeaddrinfo(server_res); /* Send cookie for verification */ - if (Nwrite(s, test->cookie, COOKIE_SIZE, Psctp) < 0) { + if (waitWrite(s, test->cookie, COOKIE_SIZE, Psctp, test, ctrl_wait_ms) != COOKIE_SIZE) { saved_errno = errno; close(s); errno = saved_errno; @@ -655,3 +686,5 @@ iperf_sctp_bindx(struct iperf_test *test, int s, int is_server) return -1; #endif /* HAVE_SCTP */ } + +#endif /* win32 */ diff --git a/src/iperf_sctp.h b/src/iperf_sctp.h index 764c410df..2848b1e9c 100644 --- a/src/iperf_sctp.h +++ b/src/iperf_sctp.h @@ -24,6 +24,8 @@ * This code is distributed under a BSD style license, see the LICENSE * file for complete information. */ +#ifndef __WIN32__ + #ifndef IPERF_SCTP_H #define IPERF_SCTP_H @@ -66,3 +68,5 @@ int iperf_sctp_init(struct iperf_test *test); int iperf_sctp_bindx(struct iperf_test *test, int s, int is_server); #endif + +#endif diff --git a/src/iperf_server_api.c b/src/iperf_server_api.c index 40d99bc1e..1ceeb359a 100644 --- a/src/iperf_server_api.c +++ b/src/iperf_server_api.c @@ -35,16 +35,18 @@ #include #include #include +#ifndef __WIN32__ #include #include #include #include #include +#include +#endif #ifdef HAVE_STDINT_H #include #endif #include -#include #include #include @@ -70,14 +72,15 @@ int iperf_server_listen(struct iperf_test *test) { retry: - if((test->listener = netannounce(test->settings->domain, Ptcp, test->bind_address, test->server_port)) < 0) { + if ((test->listener = netannounce(test->settings->domain, Ptcp, test->bind_address, test->bind_dev, + test->server_port, test)) < 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"); + iperf_err(test, "this system does not seem to support IPv6 - trying IPv4"); test->settings->domain = AF_INET; goto retry; } else { @@ -88,16 +91,19 @@ iperf_server_listen(struct iperf_test *test) if (!test->json_output) { iperf_printf(test, "-----------------------------------------------------------\n"); - iperf_printf(test, "Server listening on %d\n", test->server_port); + iperf_printf(test, "Server listening on %s %s %d\n", + test->bind_dev ? test->bind_dev : "", + test->bind_address ? test->bind_address : "", test->server_port); iperf_printf(test, "-----------------------------------------------------------\n"); if (test->forceflush) iflush(test); } + setnonblocking(test->listener, 1); + 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; + IFD_SET(test->listener, &test->read_set, test); return 0; } @@ -105,11 +111,15 @@ iperf_server_listen(struct iperf_test *test) int iperf_accept(struct iperf_test *test) { - int s; + int s = -1; signed char rbuf = ACCESS_DENIED; socklen_t len; struct sockaddr_storage addr; + if (test->debug) { + iperf_err(test, "iperf-accept called.\n"); + } + len = sizeof(addr); if ((s = accept(test->listener, (struct sockaddr *) &addr, &len)) < 0) { i_errno = IEACCEPT; @@ -119,35 +129,40 @@ iperf_accept(struct iperf_test *test) if (test->ctrl_sck == -1) { /* Server free, accept new client */ test->ctrl_sck = s; - if (Nread(test->ctrl_sck, test->cookie, COOKIE_SIZE, Ptcp) < 0) { + + setnonblocking(s, 1); + + int rv = waitRead(test->ctrl_sck, test->cookie, COOKIE_SIZE, Ptcp, test, ctrl_wait_ms); + if (rv != COOKIE_SIZE) { + iperf_err(test, "Accept problem, ctrl-sck: %d s: %d listener: %d waitRead rv: %d\n", + test->ctrl_sck, s, test->listener, rv); i_errno = IERECVCOOKIE; - return -1; + goto out_err; } - FD_SET(test->ctrl_sck, &test->read_set); - if (test->ctrl_sck > test->max_fd) test->max_fd = test->ctrl_sck; + IFD_SET(test->ctrl_sck, &test->read_set, test); if (iperf_set_send_state(test, PARAM_EXCHANGE) != 0) - return -1; + goto out_err; if (iperf_exchange_parameters(test) < 0) - return -1; + goto out_err; if (test->server_affinity != -1) if (iperf_setaffinity(test, test->server_affinity) != 0) - return -1; + goto out_err; 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. + * Just send ACCESS_DENIED, ignore any error, don't care if we cannot send the bytes immediately. */ - if (Nwrite(s, (char*) &rbuf, sizeof(rbuf), Ptcp) < 0) { - i_errno = IESENDMESSAGE; - return -1; - } - close(s); + Nwrite(s, (char*) &rbuf, sizeof(rbuf), Ptcp, test); + iclosesocket(s, test); } return 0; + +out_err: + iclosesocket(s, test); + return -1; } @@ -157,19 +172,25 @@ iperf_handle_message_server(struct iperf_test *test) { int rval; struct iperf_stream *sp; + signed char s; // 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"); + //if (test->debug) + // iperf_err(test, "Calling waitRead in handle-message-server, fd: %d", test->ctrl_sck); + if ((rval = waitRead(test->ctrl_sck, (char*) &s, sizeof(s), Ptcp, test, ctrl_wait_ms)) != sizeof(s)) { + iperf_err(test, "The client has unexpectedly closed the connection (handle-message-server): %s rval: %d", + STRERROR, rval); + if ((rval == 0) || (rval == NET_HANGUP)) { i_errno = IECTRLCLOSE; - test->state = IPERF_DONE; - return 0; + return -1; } else { i_errno = IERECVMESSAGE; return -1; } } + else { + iperf_set_state(test, s, __FUNCTION__); + } switch(test->state) { case TEST_START: @@ -179,11 +200,9 @@ iperf_handle_message_server(struct iperf_test *test) 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); + iclosesocket(sp->socket, test); + sp->socket = -1; } - test->reporter_callback(test); if (iperf_set_send_state(test, EXCHANGE_RESULTS) != 0) return -1; if (iperf_exchange_results(test) < 0) @@ -192,6 +211,7 @@ iperf_handle_message_server(struct iperf_test *test) return -1; if (test->on_test_finish) test->on_test_finish(test); + test->reporter_callback(test); break; case IPERF_DONE: break; @@ -202,18 +222,17 @@ iperf_handle_message_server(struct iperf_test *test) // ending summary statistics. signed char oldstate = test->state; cpu_util(test->cpu_util); - test->state = DISPLAY_RESULTS; + iperf_set_state(test, DISPLAY_RESULTS, __FUNCTION__); test->reporter_callback(test); - test->state = oldstate; + iperf_set_state(test, oldstate, __FUNCTION__); // 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); + iclosesocket(sp->socket, test); + sp->socket = -1; } - test->state = IPERF_DONE; + iperf_set_state(test, IPERF_DONE, __FUNCTION__); break; default: i_errno = IEMESSAGE; @@ -237,10 +256,10 @@ server_timer_proc(TimerClientData client_data, struct iperf_time *nowP) while (!SLIST_EMPTY(&test->streams)) { sp = SLIST_FIRST(&test->streams); SLIST_REMOVE_HEAD(&test->streams, streams); - close(sp->socket); + iclosesocket(sp->socket, test); iperf_free_stream(sp); } - close(test->ctrl_sck); + iclosesocket(test->ctrl_sck, test); } static void @@ -351,15 +370,20 @@ create_server_omit_timer(struct iperf_test * test) return 0; } -static void -cleanup_server(struct iperf_test *test) +void cleanup_server(struct iperf_test *test) { /* Close open test sockets */ - if (test->ctrl_sck) { - close(test->ctrl_sck); - } - if (test->listener) { - close(test->listener); + iclosesocket(test->ctrl_sck, test); + iclosesocket(test->listener, test); + iclosesocket(test->prot_listener, test); + + /* Free streams */ + struct iperf_stream *sp; + while (!SLIST_EMPTY(&test->streams)) { + sp = SLIST_FIRST(&test->streams); + SLIST_REMOVE_HEAD(&test->streams, streams); + iclosesocket(sp->socket, test); + iperf_free_stream(sp); } /* Cancel any remaining timers. */ @@ -383,6 +407,7 @@ cleanup_server(struct iperf_test *test) tmr_cancel(test->timer); test->timer = NULL; } + iperf_set_state(test, IPERF_DONE, __FUNCTION__); } @@ -400,6 +425,7 @@ iperf_run_server(struct iperf_test *test) struct iperf_time now; struct timeval* timeout; int flag; + unsigned long last_dbg = 0; if (test->logfile) if (iperf_open_logfile(test) < 0) @@ -431,7 +457,7 @@ iperf_run_server(struct iperf_test *test) // Begin calculating CPU utilization cpu_util(NULL); - test->state = IPERF_START; + iperf_set_state(test, IPERF_START, __FUNCTION__); send_streams_accepted = 0; rec_streams_accepted = 0; @@ -442,20 +468,42 @@ iperf_run_server(struct iperf_test *test) iperf_time_now(&now); timeout = tmr_timeout(&now); + if (test->debug > 1 || (test->debug && (last_dbg != now.secs))) { + if (timeout) + iperf_err(test, "timeout: %ld.%06ld max-fd: %d state: %d (%s)", + (long)(timeout->tv_sec), (long)(timeout->tv_usec), test->max_fd, + test->state, iperf_get_state_str(test->state)); + + else + iperf_err(test, "timeout NULL, max-fd: %d state: %d(%s)", test->max_fd, + test->state, iperf_get_state_str(test->state)); + print_fdset(test->max_fd, &read_set, &write_set, test); + } result = select(test->max_fd + 1, &read_set, &write_set, NULL, timeout); + + if (test->debug > 1 || (test->debug && (last_dbg != now.secs))) { + iperf_err(test, "select result: %d, listener: %d ISSET-listener: %d test-state: %d(%s)", + result, test->listener, FD_ISSET(test->listener, &read_set), test->state, + iperf_get_state_str(test->state)); + iperf_err(test, "prot-listener: %d ISSET: %d max-fd: %d", + test->prot_listener, FD_ISSET(test->prot_listener, &read_set), test->max_fd); + print_fdset(test->max_fd, &read_set, &write_set, test); + last_dbg = now.secs; + } + if (result < 0 && errno != EINTR) { - cleanup_server(test); + iperf_err(test, "Cleaning server, select had error: %s", STRERROR); i_errno = IESELECT; return -1; } + if (result > 0) { + // Check listener socket 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) { @@ -470,22 +518,29 @@ iperf_run_server(struct iperf_test *test) } } } + + // Check control socket 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; } + /* Use non-blocking IO so we don't accidentally end up + * hanging on socket operations. */ + setnonblocking(s, 1); + + if (test->debug) { + iperf_err(test, "create-streams, accepted socket: %d\n", s); + } + #if defined(HAVE_TCP_CONGESTION) if (test->protocol->id == Ptcp) { if (test->congestion) { @@ -500,12 +555,11 @@ iperf_run_server(struct iperf_test *test) * continue. */ if (errno == ENOENT) { - warning("TCP congestion control algorithm not supported"); + iperf_err(test, "TCP congestion control algorithm not supported"); } else { saved_errno = errno; - close(s); - cleanup_server(test); + iclosesocket(s, test); errno = saved_errno; i_errno = IESETCONGESTION; return -1; @@ -517,8 +571,7 @@ iperf_run_server(struct iperf_test *test) char ca[TCP_CA_NAME_MAX + 1]; if (getsockopt(s, IPPROTO_TCP, TCP_CONGESTION, ca, &len) < 0) { saved_errno = errno; - close(s); - cleanup_server(test); + iclosesocket(s, test); errno = saved_errno; i_errno = IESETCONGESTION; return -1; @@ -531,94 +584,75 @@ iperf_run_server(struct iperf_test *test) } #endif /* HAVE_TCP_CONGESTION */ - if (!is_closed(s)) { + // This code used to check if socket was is-closed, but we specifically test + // and return if it is closed above, so no need for that check here. - 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 (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 (flag != -1) { + sp = iperf_new_stream(test, s, flag); + if (!sp) { + return -1; + } - if (sp->sender) - FD_SET(s, &test->write_set); - else - FD_SET(s, &test->read_set); - - if (s > test->max_fd) test->max_fd = s; - - /* - * If the protocol isn't UDP, or even if it is but - * we're the receiver, set nonblocking sockets. - * We need this to allow a server receiver to - * maintain interactivity with the control channel. - */ - if (test->protocol->id != Pudp || - !sp->sender) { - setnonblocking(s, 1); - } + // TODO: For read+write, do we need to set this fd in both sets? + if (sp->sender) + IFD_SET(s, &test->write_set, test); + // Always set read, that way we can detect broken sockets. + IFD_SET(s, &test->read_set, test); - if (test->on_new_stream) - test->on_new_stream(sp); + if (test->on_new_stream) + test->on_new_stream(sp); - flag = -1; - } + 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); + // Stop listening for more protocol connections, we are full. + iclosesocket(test->prot_listener, test); } else { if (test->no_delay || test->settings->mss || test->settings->socket_bufsize) { - FD_CLR(test->listener, &test->read_set); - close(test->listener); - test->listener = 0; - if ((s = netannounce(test->settings->domain, Ptcp, test->bind_address, test->server_port)) < 0) { - cleanup_server(test); + // Re-open protocol listener socket, I am not sure why. --Ben + iclosesocket(test->listener, test); + if ((s = netannounce(test->settings->domain, Ptcp, test->bind_address, test->bind_dev, + test->server_port, test)) < 0) { i_errno = IELISTEN; return -1; } + + setnonblocking(s, 1); + test->listener = s; - FD_SET(test->listener, &test->read_set); - if (test->listener > test->max_fd) test->max_fd = test->listener; + IFD_SET(test->listener, &test->read_set, test); } } - test->prot_listener = -1; + 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; } } @@ -627,38 +661,48 @@ iperf_run_server(struct iperf_test *test) if (test->state == TEST_RUNNING) { if (test->mode == BIDIRECTIONAL) { if (iperf_recv(test, &read_set) < 0) { - cleanup_server(test); return -1; } if (iperf_send(test, &write_set) < 0) { - cleanup_server(test); return -1; } } else if (test->mode == SENDER) { // Reverse mode. Server sends. if (iperf_send(test, &write_set) < 0) { - cleanup_server(test); return -1; } } else { // Regular mode. Server receives. if (iperf_recv(test, &read_set) < 0) { - cleanup_server(test); return -1; } } - } - } + }/* if test is running state */ + }/* if some file descriptor has data to read/write */ if (result == 0 || (timeout != NULL && timeout->tv_sec == 0 && timeout->tv_usec == 0)) { /* Run the timers. */ + if (test->debug > 1) + iperf_err(test, "Running timers..\n"); iperf_time_now(&now); tmr_run(&now); + if (test->debug > 1) + iperf_err(test, "Done with timers..\n"); } + + if (test->state == CREATE_STREAMS) { + // If it has been too long, then consider the test a failure and return. + if (test->create_streams_state_at + 5000 < getCurMs()) { + iperf_err(test, "Test has been in create-streams state for: %llums, aborting.\n", + (unsigned long long)(getCurMs() - test->create_streams_state_at)); + return -1; + } + } } - cleanup_server(test); + if (test->debug) + iperf_err(test, "Done with server loop, cleaning up server.\n"); if (test->json_output) { if (iperf_json_finish(test) < 0) diff --git a/src/iperf_tcp.c b/src/iperf_tcp.c index 232aaa169..454cfaaa8 100644 --- a/src/iperf_tcp.c +++ b/src/iperf_tcp.c @@ -29,12 +29,14 @@ #include #include #include +#ifndef __WIN32__ #include #include #include #include -#include #include +#endif +#include #include #include "iperf.h" @@ -56,10 +58,13 @@ iperf_tcp_recv(struct iperf_stream *sp) { int r; - r = Nread(sp->socket, sp->buffer, sp->settings->blksize, Ptcp); + r = Nread(sp->socket, sp->buffer, sp->settings->blksize, Ptcp, sp->test); - if (r < 0) + if (r < 0) { + if (sp->test->debug) // normal to hit this in tcp client receive mode. + iperf_err(sp->test, "tcp-recv, failed (%s), socket: %d", STRERROR, sp->socket); return r; + } /* Only count bytes received while we're in the correct state. */ if (sp->test->state == TEST_RUNNING) { @@ -83,11 +88,21 @@ int iperf_tcp_send(struct iperf_stream *sp) { int r; + int wsize = sp->settings->blksize; - if (sp->test->zerocopy) - r = Nsendfile(sp->buffer_fd, sp->socket, sp->buffer, sp->settings->blksize); - else - r = Nwrite(sp->socket, sp->buffer, sp->settings->blksize, Ptcp); + if (sp->test->zerocopy) { + r = Nsendfile(sp->buffer_fd, sp->socket, sp->buffer, wsize); + } + else { +#ifdef __WIN32__ + if (sp->settings->mss) { + // Windows cannot do mss, send smaller block-sizes instead. + if (sp->settings->mss < sp->settings->blksize) + wsize = sp->settings->mss; + } +#endif + r = Nwrite(sp->socket, sp->buffer, wsize, Ptcp, sp->test); + } if (r < 0) return r; @@ -95,8 +110,8 @@ iperf_tcp_send(struct iperf_stream *sp) sp->result->bytes_sent += r; sp->result->bytes_sent_this_interval += r; - if (sp->test->debug) - printf("sent %d bytes of %d, total %" PRIu64 "\n", r, sp->settings->blksize, sp->result->bytes_sent); + if (sp->test->debug > 1) + printf("tcp: sent %d bytes of %d, total %llu\n", r, wsize, (long long unsigned)sp->result->bytes_sent); return r; } @@ -116,22 +131,28 @@ iperf_tcp_accept(struct iperf_test * test) struct sockaddr_storage addr; len = sizeof(addr); + + /* Wait a bit until the peer attempts a connection */ + waitSocketReadable(test->listener, ctrl_wait_ms); + if ((s = accept(test->listener, (struct sockaddr *) &addr, &len)) < 0) { + iperf_err(test, "tcp-accept, accept failed: %s", STRERROR); i_errno = IESTREAMCONNECT; return -1; } - if (Nread(s, cookie, COOKIE_SIZE, Ptcp) < 0) { + if (waitRead(s, cookie, COOKIE_SIZE, Ptcp, test, ctrl_wait_ms) != COOKIE_SIZE) { i_errno = IERECVCOOKIE; return -1; } if (strcmp(test->cookie, cookie) != 0) { - if (Nwrite(s, (char*) &rbuf, sizeof(rbuf), Ptcp) < 0) { + if (waitWrite(s, (char*) &rbuf, sizeof(rbuf), Ptcp, test, ctrl_wait_ms) != sizeof(rbuf)) { i_errno = IESENDMESSAGE; return -1; } - close(s); + iclosesocket(s, test); + return -1; } return s; @@ -141,6 +162,7 @@ iperf_tcp_accept(struct iperf_test * test) /* iperf_tcp_listen * * start up a listener for TCP stream connections + * Returns non-blocking socket. */ int iperf_tcp_listen(struct iperf_test *test) @@ -165,8 +187,7 @@ iperf_tcp_listen(struct iperf_test *test) struct addrinfo hints, *res; char portstr[6]; - FD_CLR(s, &test->read_set); - close(s); + iclosesocket(s, test); snprintf(portstr, 6, "%d", test->server_port); memset(&hints, 0, sizeof(hints)); @@ -195,11 +216,28 @@ iperf_tcp_listen(struct iperf_test *test) return -1; } + setnonblocking(s, 1); + + if (test->bind_dev) { +#ifdef SO_BINDTODEVICE + if (setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE, + test->bind_dev, IFNAMSIZ) < 0) +#endif + { + saved_errno = errno; + iclosesocket(s, test); + freeaddrinfo(res); + i_errno = IEBINDDEV; + errno = saved_errno; + return -1; + } + } + if (test->no_delay) { opt = 1; - if (setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt)) < 0) { + if (setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (const char*)&opt, sizeof(opt)) < 0) { saved_errno = errno; - close(s); + iclosesocket(s, test); freeaddrinfo(res); errno = saved_errno; i_errno = IESETNODELAY; @@ -207,61 +245,66 @@ iperf_tcp_listen(struct iperf_test *test) } } // XXX: Setting MSS is very buggy! +#ifndef __WIN32__ if ((opt = test->settings->mss)) { if (setsockopt(s, IPPROTO_TCP, TCP_MAXSEG, &opt, sizeof(opt)) < 0) { saved_errno = errno; - close(s); + iclosesocket(s, test); freeaddrinfo(res); errno = saved_errno; i_errno = IESETMSS; return -1; } } +#endif if ((opt = test->settings->socket_bufsize)) { - if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt)) < 0) { + if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, (const char*)&opt, sizeof(opt)) < 0) { saved_errno = errno; - close(s); + iclosesocket(s, test); freeaddrinfo(res); errno = saved_errno; i_errno = IESETBUF; return -1; } - if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, &opt, sizeof(opt)) < 0) { + if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, (const char*)&opt, sizeof(opt)) < 0) { saved_errno = errno; - close(s); + iclosesocket(s, test); 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"); - } - } - } + /* 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) { + iperf_err(test, "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); - } - } - } + + { + 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) { + if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char*)&opt, sizeof(opt)) < 0) { saved_errno = errno; - close(s); + iclosesocket(s, test); freeaddrinfo(res); errno = saved_errno; i_errno = IEREUSEADDR; @@ -280,9 +323,9 @@ iperf_tcp_listen(struct iperf_test *test) else opt = 1; if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, - (char *) &opt, sizeof(opt)) < 0) { + (const char *) &opt, sizeof(opt)) < 0) { saved_errno = errno; - close(s); + iclosesocket(s, test); freeaddrinfo(res); errno = saved_errno; i_errno = IEV6ONLY; @@ -293,7 +336,7 @@ iperf_tcp_listen(struct iperf_test *test) if (bind(s, (struct sockaddr *) res->ai_addr, res->ai_addrlen) < 0) { saved_errno = errno; - close(s); + iclosesocket(s, test); freeaddrinfo(res); errno = saved_errno; i_errno = IESTREAMLISTEN; @@ -304,6 +347,9 @@ iperf_tcp_listen(struct iperf_test *test) if (listen(s, INT_MAX) < 0) { i_errno = IESTREAMLISTEN; + saved_errno = errno; + iclosesocket(s, test); + errno = saved_errno; return -1; } @@ -312,13 +358,15 @@ iperf_tcp_listen(struct iperf_test *test) /* Read back and verify the sender socket buffer size */ optlen = sizeof(sndbuf_actual); - if (getsockopt(s, SOL_SOCKET, SO_SNDBUF, &sndbuf_actual, &optlen) < 0) { + if (getsockopt(s, SOL_SOCKET, SO_SNDBUF, (char*)&sndbuf_actual, &optlen) < 0) { saved_errno = errno; - close(s); + iclosesocket(s, test); + test->listener = -1; errno = saved_errno; i_errno = IESETBUF; return -1; } + if (test->debug) { printf("SNDBUF is %u, expecting %u\n", sndbuf_actual, test->settings->socket_bufsize); } @@ -329,9 +377,10 @@ iperf_tcp_listen(struct iperf_test *test) /* Read back and verify the receiver socket buffer size */ optlen = sizeof(rcvbuf_actual); - if (getsockopt(s, SOL_SOCKET, SO_RCVBUF, &rcvbuf_actual, &optlen) < 0) { + if (getsockopt(s, SOL_SOCKET, SO_RCVBUF, (char *)&rcvbuf_actual, &optlen) < 0) { saved_errno = errno; - close(s); + iclosesocket(s, test); + test->listener = -1; errno = saved_errno; i_errno = IESETBUF; return -1; @@ -360,11 +409,12 @@ iperf_tcp_listen(struct iperf_test *test) * 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. + * Returns non-blocking socket */ int iperf_tcp_connect(struct iperf_test *test) { - struct addrinfo hints, *local_res, *server_res; + struct addrinfo hints, *local_res = NULL, *server_res = NULL; char portstr[6]; int s, opt; socklen_t optlen; @@ -376,6 +426,7 @@ iperf_tcp_connect(struct iperf_test *test) hints.ai_family = test->settings->domain; hints.ai_socktype = SOCK_STREAM; if ((gerror = getaddrinfo(test->bind_address, NULL, &hints, &local_res)) != 0) { + iperf_err(test, "tcp-connect, getaddrinfo failed: %s", STRERROR); i_errno = IESTREAMCONNECT; return -1; } @@ -388,18 +439,37 @@ iperf_tcp_connect(struct iperf_test *test) if ((gerror = getaddrinfo(test->server_hostname, portstr, &hints, &server_res)) != 0) { if (test->bind_address) freeaddrinfo(local_res); + iperf_err(test, "tcp-connect, getaddrinfo (server) failed: %s", STRERROR); i_errno = IESTREAMCONNECT; return -1; } if ((s = socket(server_res->ai_family, SOCK_STREAM, 0)) < 0) { - if (test->bind_address) - freeaddrinfo(local_res); + freeaddrinfo(local_res); freeaddrinfo(server_res); + iperf_err(test, "tcp-connect, socket() failed: %s", STRERROR); i_errno = IESTREAMCONNECT; return -1; } + setnonblocking(s, 1); + + if (test->bind_dev) { +#ifdef SO_BINDTODEVICE + if (setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE, + test->bind_dev, IFNAMSIZ) < 0) +#endif + { + saved_errno = errno; + iclosesocket(s, test); + 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). @@ -410,8 +480,9 @@ iperf_tcp_connect(struct iperf_test *test) lcladdr->sin_port = htons(test->bind_port); if (bind(s, (struct sockaddr *) local_res->ai_addr, local_res->ai_addrlen) < 0) { + iperf_err(test, "tcp-connect, bind() failed: %s\n", STRERROR); saved_errno = errno; - close(s); + iclosesocket(s, test); freeaddrinfo(local_res); freeaddrinfo(server_res); errno = saved_errno; @@ -444,7 +515,7 @@ iperf_tcp_connect(struct iperf_test *test) /* Unknown protocol */ else { saved_errno = errno; - close(s); + iclosesocket(s, test); freeaddrinfo(server_res); errno = saved_errno; i_errno = IEPROTOCOL; @@ -452,8 +523,9 @@ iperf_tcp_connect(struct iperf_test *test) } if (bind(s, (struct sockaddr *) &lcl, addrlen) < 0) { + iperf_err(test, "tcp-connect, bind2() failed: %s\n", STRERROR); saved_errno = errno; - close(s); + iclosesocket(s, test); freeaddrinfo(server_res); errno = saved_errno; i_errno = IESTREAMCONNECT; @@ -464,37 +536,39 @@ iperf_tcp_connect(struct iperf_test *test) /* Set socket options */ if (test->no_delay) { opt = 1; - if (setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt)) < 0) { + if (setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (const char*)&opt, sizeof(opt)) < 0) { saved_errno = errno; - close(s); + iclosesocket(s, test); freeaddrinfo(server_res); errno = saved_errno; i_errno = IESETNODELAY; return -1; } } +#ifndef __WIN32__ if ((opt = test->settings->mss)) { if (setsockopt(s, IPPROTO_TCP, TCP_MAXSEG, &opt, sizeof(opt)) < 0) { saved_errno = errno; - close(s); + iclosesocket(s, test); freeaddrinfo(server_res); errno = saved_errno; i_errno = IESETMSS; return -1; } } +#endif if ((opt = test->settings->socket_bufsize)) { - if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt)) < 0) { + if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, (const char*)&opt, sizeof(opt)) < 0) { saved_errno = errno; - close(s); + iclosesocket(s, test); freeaddrinfo(server_res); errno = saved_errno; i_errno = IESETBUF; return -1; } - if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, &opt, sizeof(opt)) < 0) { + if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, (const char*)&opt, sizeof(opt)) < 0) { saved_errno = errno; - close(s); + iclosesocket(s, test); freeaddrinfo(server_res); errno = saved_errno; i_errno = IESETBUF; @@ -504,9 +578,9 @@ iperf_tcp_connect(struct iperf_test *test) /* Read back and verify the sender socket buffer size */ optlen = sizeof(sndbuf_actual); - if (getsockopt(s, SOL_SOCKET, SO_SNDBUF, &sndbuf_actual, &optlen) < 0) { + if (getsockopt(s, SOL_SOCKET, SO_SNDBUF, (char*)&sndbuf_actual, &optlen) < 0) { saved_errno = errno; - close(s); + iclosesocket(s, test); freeaddrinfo(server_res); errno = saved_errno; i_errno = IESETBUF; @@ -522,9 +596,9 @@ iperf_tcp_connect(struct iperf_test *test) /* Read back and verify the receiver socket buffer size */ optlen = sizeof(rcvbuf_actual); - if (getsockopt(s, SOL_SOCKET, SO_RCVBUF, &rcvbuf_actual, &optlen) < 0) { + if (getsockopt(s, SOL_SOCKET, SO_RCVBUF, (char*)&rcvbuf_actual, &optlen) < 0) { saved_errno = errno; - close(s); + iclosesocket(s, test); freeaddrinfo(server_res); errno = saved_errno; i_errno = IESETBUF; @@ -548,7 +622,7 @@ iperf_tcp_connect(struct iperf_test *test) if (test->settings->flowlabel) { if (server_res->ai_addr->sa_family != AF_INET6) { saved_errno = errno; - close(s); + iclosesocket(s, test); freeaddrinfo(server_res); errno = saved_errno; i_errno = IESETFLOW; @@ -568,7 +642,7 @@ iperf_tcp_connect(struct iperf_test *test) if (setsockopt(s, IPPROTO_IPV6, IPV6_FLOWLABEL_MGR, freq, freq_len) < 0) { saved_errno = errno; - close(s); + iclosesocket(s, test); freeaddrinfo(server_res); errno = saved_errno; i_errno = IESETFLOW; @@ -579,7 +653,7 @@ iperf_tcp_connect(struct iperf_test *test) opt = 1; if (setsockopt(s, IPPROTO_IPV6, IPV6_FLOWINFO_SEND, &opt, sizeof(opt)) < 0) { saved_errno = errno; - close(s); + iclosesocket(s, test); freeaddrinfo(server_res); errno = saved_errno; i_errno = IESETFLOW; @@ -599,7 +673,7 @@ iperf_tcp_connect(struct iperf_test *test) 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"); + iperf_err(test, "Unable to set socket pacing"); } } } @@ -613,9 +687,10 @@ iperf_tcp_connect(struct iperf_test *test) } } - if (connect(s, (struct sockaddr *) server_res->ai_addr, server_res->ai_addrlen) < 0 && errno != EINPROGRESS) { + if (connect(s, (struct sockaddr *) server_res->ai_addr, server_res->ai_addrlen) < 0 && !eWouldBlock()) { + iperf_err(test, "tcp-connect, connect() failed: %s\n", STRERROR); saved_errno = errno; - close(s); + iclosesocket(s, test); freeaddrinfo(server_res); errno = saved_errno; i_errno = IESTREAMCONNECT; @@ -625,9 +700,9 @@ iperf_tcp_connect(struct iperf_test *test) freeaddrinfo(server_res); /* Send cookie for verification */ - if (Nwrite(s, test->cookie, COOKIE_SIZE, Ptcp) < 0) { + if (waitWrite(s, test->cookie, COOKIE_SIZE, Ptcp, test, ctrl_wait_ms) != COOKIE_SIZE) { saved_errno = errno; - close(s); + iclosesocket(s, test); errno = saved_errno; i_errno = IESENDCOOKIE; return -1; diff --git a/src/iperf_time.c b/src/iperf_time.c index 5f94dc011..11bd9a57b 100644 --- a/src/iperf_time.c +++ b/src/iperf_time.c @@ -65,6 +65,16 @@ iperf_time_now(struct iperf_time *time1) #endif +uint64_t getCurMs() { + struct iperf_time tv; + uint64_t rv; + iperf_time_now(&tv); + rv = tv.secs * 1000; + rv += tv.usecs / 1000; + return rv; +} + + /* iperf_time_add_usecs * * Add a number of microseconds to a iperf_time. diff --git a/src/iperf_time.h b/src/iperf_time.h index 588ee2624..10c668ab3 100644 --- a/src/iperf_time.h +++ b/src/iperf_time.h @@ -46,4 +46,6 @@ uint64_t iperf_time_in_usecs(struct iperf_time *time); double iperf_time_in_secs(struct iperf_time *time); +uint64_t getCurMs(); + #endif diff --git a/src/iperf_udp.c b/src/iperf_udp.c index 3d37dab67..6ccda65f3 100644 --- a/src/iperf_udp.c +++ b/src/iperf_udp.c @@ -30,14 +30,16 @@ #include #include #include +#ifndef __WIN32__ #include #include #include +#include +#endif #ifdef HAVE_STDINT_H #include #endif #include -#include #include "iperf.h" #include "iperf_api.h" @@ -68,7 +70,7 @@ iperf_udp_recv(struct iperf_stream *sp) double transit = 0, d = 0; struct iperf_time sent_time, arrival_time, temp_time; - r = Nread(sp->socket, sp->buffer, size, Pudp); + r = Nread(sp->socket, sp->buffer, size, Pudp, sp->test); /* * If we got an error in the read, or if we didn't read anything @@ -105,8 +107,8 @@ iperf_udp_recv(struct iperf_stream *sp) sent_time.usecs = usec; } - if (sp->test->debug) - fprintf(stderr, "pcount %" PRIu64 " packet_count %d\n", pcount, sp->packet_count); + if (sp->test->debug > 1) + iperf_err(sp->test, "udp pcount %" PRIu64 " packet_count %d\n", pcount, sp->packet_count); /* * Try to handle out of order packets. The way we do this @@ -149,7 +151,8 @@ iperf_udp_recv(struct iperf_stream *sp) /* Log the out-of-order packet */ if (sp->test->debug) - fprintf(stderr, "OUT OF ORDER - incoming packet sequence %" PRIu64 " but expected sequence %d on stream %d", pcount, sp->packet_count, sp->socket); + iperf_err(sp->test, "OUT OF ORDER - incoming packet sequence %" PRIu64 " but expected sequence %d on stream %d", + pcount, sp->packet_count, sp->socket); } /* @@ -167,11 +170,16 @@ iperf_udp_recv(struct iperf_stream *sp) iperf_time_diff(&arrival_time, &sent_time, &temp_time); transit = iperf_time_in_secs(&temp_time); - d = transit - sp->prev_transit; - if (d < 0) - d = -d; - sp->prev_transit = transit; - sp->jitter += (d - sp->jitter) / 16.0; + if (sp->prev_transit) { + d = transit - sp->prev_transit; + if (d < 0) + d = -d; + sp->jitter += (d - sp->jitter) / 16.0; + } + else { + sp->jitter = 0; + } + sp->prev_transit = transit; } else { if (sp->test->debug) @@ -225,7 +233,7 @@ iperf_udp_send(struct iperf_stream *sp) } - r = Nwrite(sp->socket, sp->buffer, size, Pudp); + r = Nwrite(sp->socket, sp->buffer, size, Pudp, sp->test); if (r < 0) return r; @@ -233,8 +241,8 @@ iperf_udp_send(struct iperf_stream *sp) sp->result->bytes_sent += r; sp->result->bytes_sent_this_interval += r; - if (sp->test->debug) - printf("sent %d bytes of %d, total %" PRIu64 "\n", r, sp->settings->blksize, sp->result->bytes_sent); + if (sp->test->debug > 1) + printf("udp: sent %d bytes of %d, total %" PRIu64 "\n", r, sp->settings->blksize, sp->result->bytes_sent); return r; } @@ -270,11 +278,11 @@ iperf_udp_buffercheck(struct iperf_test *test, int s) socklen_t optlen; if ((opt = test->settings->socket_bufsize)) { - if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt)) < 0) { + if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, (char*)&opt, sizeof(opt)) < 0) { i_errno = IESETBUF; return -1; } - if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, &opt, sizeof(opt)) < 0) { + if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, (char*)&opt, sizeof(opt)) < 0) { i_errno = IESETBUF; return -1; } @@ -282,45 +290,39 @@ iperf_udp_buffercheck(struct iperf_test *test, int 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) { + if (getsockopt(s, SOL_SOCKET, SO_SNDBUF, (char*)&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); + printf("SNDBUF is %u, expecting %u, fd: %d\n", sndbuf_actual, test->settings->socket_bufsize, s); } if (test->settings->socket_bufsize && test->settings->socket_bufsize > sndbuf_actual) { i_errno = IESETBUF2; return -1; } if (test->settings->blksize > sndbuf_actual) { - char str[80]; - snprintf(str, sizeof(str), - "Block size %d > sending socket buffer size %d", - test->settings->blksize, sndbuf_actual); - warning(str); + iperf_err(test, "Block size %d > sending socket buffer size %d", + test->settings->blksize, sndbuf_actual); 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) { + if (getsockopt(s, SOL_SOCKET, SO_RCVBUF, (char*)&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); + printf("RCVBUF is %u, expecting %u fd: %d\n", rcvbuf_actual, test->settings->socket_bufsize, s); } if (test->settings->socket_bufsize && test->settings->socket_bufsize > rcvbuf_actual) { i_errno = IESETBUF2; return -1; } if (test->settings->blksize > rcvbuf_actual) { - char str[80]; - snprintf(str, sizeof(str), - "Block size %d > receiving socket buffer size %d", - test->settings->blksize, rcvbuf_actual); - warning(str); + iperf_err(test, "Block size %d > receiving socket buffer size %d", + test->settings->blksize, rcvbuf_actual); rc = 1; } @@ -346,6 +348,7 @@ iperf_udp_accept(struct iperf_test *test) socklen_t len; int sz, s; int rc; + int i; /* * Get the current outstanding socket. This socket will be used to handle @@ -353,17 +356,45 @@ iperf_udp_accept(struct iperf_test *test) */ 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; + for (i = 0; i<30; i++) { + fd_set read_fds; + FD_ZERO(&read_fds); //Zero out the file descriptor set + FD_SET(test->prot_listener, &read_fds); //Set the current socket file descriptor into the set + + /* Don't block forever if the peer messed up or cannot otherwise send frame + * this direction. + */ + struct timeval tv; + tv.tv_sec = 1; //The second portion of the struct + tv.tv_usec = 0; //The microsecond portion of the struct + + int select_ret = select(test->prot_listener + 1, &read_fds, NULL, NULL, &tv); + if (select_ret == 1) { + /* + * 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, (char*)&buf, sizeof(buf), 0, (struct sockaddr *) &sa_peer, &len)) < 0) { + i_errno = IESTREAMACCEPT; + return -1; + } + goto got_response; + } + else { + if (test->debug) { + iperf_err(test, "Did not receive response, try %d / 30, in udp-accept.", i); + } + } } + /* If here, we did not get a response in time. */ + iperf_err(test, "Did not receive frame within 30 seconds in udp-accept."); + i_errno = IESTREAMACCEPT; + return -1; + +got_response: if (connect(s, (struct sockaddr *) &sa_peer, len) < 0) { i_errno = IESTREAMACCEPT; return -1; @@ -374,6 +405,7 @@ iperf_udp_accept(struct iperf_test *test) 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. @@ -400,7 +432,7 @@ iperf_udp_accept(struct iperf_test *test) 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"); + iperf_err(test, "Unable to set socket pacing"); } } } @@ -417,19 +449,30 @@ iperf_udp_accept(struct iperf_test *test) /* * 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->server_port); + test->prot_listener = netannounce(test->settings->domain, Pudp, test->bind_address, test->bind_dev, + test->server_port, test); + if (test->debug) { + iperf_err(test, "udp-accept, new prot-listener socket: %d", test->prot_listener); + } + if (test->prot_listener < 0) { i_errno = IESTREAMLISTEN; + iclosesocket(s, test); 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; + IFD_SET(test->prot_listener, &test->read_set, test); /* Let the client know we're ready "accept" another UDP "stream" */ buf = 987654321; /* any content will work here */ - if (write(s, &buf, sizeof(buf)) < 0) { +#ifdef __WIN32__ + if (send(s, (const char*)&buf, sizeof(buf), 0) < 0) +#else + if (write(s, &buf, sizeof(buf)) < 0) +#endif + { i_errno = IESTREAMWRITE; + iclosesocket(s, test); return -1; } @@ -449,11 +492,16 @@ iperf_udp_listen(struct iperf_test *test) { int s; - if ((s = netannounce(test->settings->domain, Pudp, test->bind_address, test->server_port)) < 0) { + if ((s = netannounce(test->settings->domain, Pudp, test->bind_address, test->bind_dev, + test->server_port, test)) < 0) { i_errno = IESTREAMLISTEN; return -1; } + if (test->debug) { + iperf_err(test, "iperf-udp-listen, fd: %d", s); + } + /* * The caller will put this value into test->prot_listener. */ @@ -470,22 +518,32 @@ int iperf_udp_connect(struct iperf_test *test) { int s, buf, sz; + int i; #ifdef SO_RCVTIMEO struct timeval tv; #endif int rc; + if (test->debug) { + iperf_err(test, "udp-connect called"); + } + /* Create and bind our local socket. */ - if ((s = netdial(test->settings->domain, Pudp, test->bind_address, test->bind_port, test->server_hostname, test->server_port, -1)) < 0) { + if ((s = netdial(test->settings->domain, Pudp, test->bind_address, test->bind_dev, test->bind_port, + test->server_hostname, test->server_port, -1, test)) < 0) { + iperf_err(test, "udp-connect, netdial() failed: %s", STRERROR); i_errno = IESTREAMCONNECT; return -1; } /* Check and set socket buffer sizes */ rc = iperf_udp_buffercheck(test, s); - if (rc < 0) + if (rc < 0) { /* error */ + iclosesocket(s, test); return rc; + } + /* * If the socket buffer was too small, but it was the default * size, then try explicitly setting it to something larger. @@ -497,8 +555,10 @@ iperf_udp_connect(struct iperf_test *test) bufsize); test->settings->socket_bufsize = bufsize; rc = iperf_udp_buffercheck(test, s); - if (rc < 0) + if (rc < 0) { + iclosesocket(s, test); return rc; + } } } @@ -512,7 +572,7 @@ iperf_udp_connect(struct iperf_test *test) 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"); + iperf_err(test, "Unable to set socket pacing"); } } } @@ -530,7 +590,7 @@ iperf_udp_connect(struct iperf_test *test) /* 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)); + setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (char*)&tv, sizeof(struct timeval)); #endif /* @@ -538,21 +598,70 @@ iperf_udp_connect(struct iperf_test *test) * The server learns our address by obtaining its peer's address. */ buf = 123456789; /* this can be pretty much anything */ - if (write(s, &buf, sizeof(buf)) < 0) { - // XXX: Should this be changed to IESTREAMCONNECT? - i_errno = IESTREAMWRITE; - return -1; + if (test->debug) { + iperf_err(test, "sending '123456789' to peer to let them know we are here: %s", + hexdump((const unsigned char*)(&buf), sizeof(buf), 1, 1)); } - /* - * Wait until the server replies back to us. - */ - if ((sz = recv(s, &buf, sizeof(buf), 0)) < 0) { - i_errno = IESTREAMREAD; - return -1; + for (i = 0; i<30; i++) { + fd_set read_fds; + + /* UDP messages can be lost and will not automatically be retransmitted. + * so we will retry and use select to not block forever. + */ +#ifdef __WIN32__ + if (send(s, (const char*)&buf, sizeof(buf), 0) < 0) +#else + if (write(s, &buf, sizeof(buf)) < 0) +#endif + { + // XXX: Should this be changed to IESTREAMCONNECT? + i_errno = IESTREAMWRITE; + iclosesocket(s, test); + return -1; + } + + if (test->debug) { + iperf_err(test, "waiting to receive response from server"); + } + + /* + * Wait until the server replies back to us. + */ + //We are going to use select to wait for the socket to connect + struct timeval tv; + tv.tv_sec = 1; + tv.tv_usec = 0; + + FD_ZERO(&read_fds); + FD_SET(s, &read_fds); + + int select_ret = select(s + 1, &read_fds, NULL, NULL, &tv); + if (select_ret == 1) { + if ((sz = recv(s, (char*)&buf, sizeof(buf), 0)) < 0) { + iperf_err(test, "Failed recv: %s socket: %d", STRERROR, s); + iclosesocket(s, test); + i_errno = IESTREAMREAD; + return -1; + } + + if (test->debug) { + iperf_err(test, "Received response from server: %s", hexdump((const unsigned char*)(&buf), sizeof(buf), 1, 1)); + } + return s; + } + else { + if (test->debug) { + iperf_err(test, "No response from server, will retry: %d / 30", i); + } + } } - return s; + /* if here, we could not get a response in time. */ + iperf_err(test, "Did not receive UDP connect response in time."); + iclosesocket(s, test); + i_errno = IESTREAMREAD; + return -1; } diff --git a/src/iperf_util.c b/src/iperf_util.c index 412397a08..1f3a11210 100644 --- a/src/iperf_util.c +++ b/src/iperf_util.c @@ -37,11 +37,14 @@ #include #include #include +#ifndef __WIN32__ +#include #include #include #include #include #include +#endif #include #include #include @@ -65,10 +68,19 @@ int readentropy(void *out, size_t outsize) if (frandom == NULL) { frandom = fopen(rndfile, "rb"); if (frandom == NULL) { - iperf_errexit(NULL, "error - failed to open %s: %s\n", - rndfile, strerror(errno)); + // use rand() instead (this could be way more efficient but doesn't matter much) + unsigned char* dest = (unsigned char*)out; + size_t i; + for (i = 0; i= maxlen) + return retval; + + if (add_newlines && ((i + 1) % bytes_per_line == 0)) { + if (show_decode) { + int j; + count = snprintf(buf, maxlen - sofar, " "); + buf += count; + if (sofar >= maxlen) + return retval; + for (j = i - (bytes_per_line - 1); j<=i; j++) { + if (isprint(msg[j])) { + count = snprintf(buf, maxlen - sofar, "%c", msg[j]); + } + else { + count = snprintf(buf, maxlen - sofar, "."); + } + if (sofar >= maxlen) + return retval; + buf += count; + }//for + } + count = snprintf(buf, maxlen - sofar, "\n"); + if (sofar >= maxlen) + return retval; + buf += count; + }//if + }//for + + if (show_decode) { + // do final char translations. + int q = (i) % bytes_per_line; + int offset = i-q; + int l, j; + count = snprintf(buf, maxlen - sofar, " "); + buf += count; + if (sofar >= maxlen) + return retval; + for (l = 0; l< bytes_per_line-q; l++) { + //space, where the hex would have gone. + count = snprintf(buf, maxlen - sofar, " "); + buf += count; + if (sofar >= maxlen) + return retval; + } -/* 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; + //VLOG << "q: " << q << " offset: " << offset << " i: " << i << endl; + for (j = 0; j= maxlen) + return retval; + buf += count; + }//for + snprintf(buf, maxlen - sofar, "\n"); } - return 0; -} + return retval; +}//hexdump double @@ -189,6 +256,13 @@ timeval_diff(struct timeval * tv0, struct timeval * tv1) void cpu_util(double pcpu[3]) { +#ifdef __WIN32__ + if (pcpu) { + pcpu[0] = 0; + pcpu[1] = 0; + pcpu[2] = 0; + } +#else static struct iperf_time last; static clock_t clast; static struct rusage rlast; @@ -221,11 +295,15 @@ cpu_util(double pcpu[3]) pcpu[0] = (((ctemp - clast) * 1000000.0 / CLOCKS_PER_SEC) / timediff) * 100; pcpu[1] = (userdiff / timediff) * 100; pcpu[2] = (systemdiff / timediff) * 100; +#endif } const char * get_system_info(void) { +#ifdef __WIN32__ + return "Windows"; +#else static char buf[1024]; struct utsname uts; @@ -236,6 +314,7 @@ get_system_info(void) uts.release, uts.version, uts.machine); return buf; +#endif } @@ -425,6 +504,7 @@ iperf_dump_fdset(FILE *fp, char *str, int nfds, fd_set *fds) * Cobbled together from various daemon(3) implementations, * not intended to be general-purpose. */ #ifndef HAVE_DAEMON +#ifndef __WIN32__ int daemon(int nochdir, int noclose) { pid_t pid = 0; @@ -482,6 +562,7 @@ int daemon(int nochdir, int noclose) return (0); } #endif /* HAVE_DAEMON */ +#endif /* Compatibility version of getline(3) for systems that don't have it.. */ #ifndef HAVE_GETLINE diff --git a/src/iperf_util.h b/src/iperf_util.h index 76bfd20fe..87d77f303 100644 --- a/src/iperf_util.h +++ b/src/iperf_util.h @@ -29,8 +29,14 @@ #include "iperf_config.h" #include "cjson.h" +#ifndef __WIN32__ #include +#endif #include +#include "iperf.h" + +const char* hexdump(const unsigned char* msg, int len, int show_decode, + int add_newlines); int readentropy(void *out, size_t outsize); @@ -38,8 +44,6 @@ 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); diff --git a/src/libiperf.3 b/src/libiperf.3 index e88516d86..a91e882c7 100644 --- a/src/libiperf.3 +++ b/src/libiperf.3 @@ -25,6 +25,7 @@ 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 ); diff --git a/src/main.c b/src/main.c index fe10a2f4d..81a0c898f 100644 --- a/src/main.c +++ b/src/main.c @@ -36,11 +36,7 @@ #ifdef HAVE_STDINT_H #include #endif -#include #include -#include -#include -#include #include "iperf.h" #include "iperf_api.h" @@ -58,6 +54,20 @@ main(int argc, char **argv) { struct iperf_test *test; +#ifdef __WIN32__ + WORD wVersionRequested; + WSADATA wsaData; + int err; + + wVersionRequested = MAKEWORD( 2, 0 ); + if ((err = WSAStartup( wVersionRequested, &wsaData )) != 0) { + /* Tell the user that we could not find a usable */ + /* WinSock DLL. */ + fprintf(stderr, "ERROR: Could not load Winsock 2.0 DLLs, err: %d\n", err); + return err; + } +#endif + // XXX: Setting the process affinity requires root on most systems. // Is this a feature we really need? #ifdef TEST_PROC_AFFINITY @@ -93,6 +103,7 @@ main(int argc, char **argv) #endif test = iperf_new_test(); + if (!test) iperf_errexit(NULL, "create new test error - %s", iperf_strerror(i_errno)); iperf_defaults(test); /* sets defaults */ @@ -130,22 +141,29 @@ run(struct iperf_test *test) if (setjmp(sigend_jmp_buf)) iperf_got_sigend(test); +#ifndef __WIN32__ /* Ignore SIGPIPE to simplify error handling */ signal(SIGPIPE, SIG_IGN); +#endif + + if (iperf_create_pidfile(test) < 0) { + i_errno = IEPIDFILE; + iperf_errexit(test, "error - %s", iperf_strerror(i_errno)); + } switch (test->role) { case 's': if (test->daemon) { +#ifndef __WIN32__ 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)); +#else + iperf_errexit(test, "daemon mode not supported on windows."); +#endif } for (;;) { int rc; @@ -156,6 +174,10 @@ run(struct iperf_test *test) iperf_errexit(test, "exiting"); } } + else { + iperf_err(test, "Finished with iperf_run_srver.."); + } + cleanup_server(test); iperf_reset_test(test); if (iperf_get_test_one_off(test)) { /* Authentication failure doesn't count for 1-off test */ @@ -165,7 +187,6 @@ run(struct iperf_test *test) break; } } - iperf_delete_pidfile(test); break; case 'c': if (iperf_run_client(test) < 0) @@ -176,8 +197,22 @@ run(struct iperf_test *test) break; } + iperf_delete_pidfile(test); + iperf_catch_sigend(SIG_DFL); +#ifndef __WIN32__ signal(SIGPIPE, SIG_DFL); +#endif return 0; } + +#ifdef __WIN32__ +const char* winstrerror() { + // To decode these, try: + // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winsock/winsock/windows_sockets_error_codes_2.asp + static char lfsrr[128]; + sprintf(lfsrr, "errno: %s(%d) (WSAGetLastError: Error# %d)", strerror(errno), errno, WSAGetLastError()); + return lfsrr; +} +#endif diff --git a/src/net.c b/src/net.c index 96fb7ede1..94921a24d 100644 --- a/src/net.c +++ b/src/net.c @@ -29,12 +29,14 @@ #include #include #include +#ifndef __WIN32__ #include #include #include #include -#include #include +#endif +#include #include #include #include @@ -60,9 +62,11 @@ #include #endif /* HAVE_POLL_H */ +#include "iperf.h" #include "iperf_util.h" #include "net.h" #include "timer.h" +#include "iperf_api.h" /* * Declaration of gerror in iperf_error.c. Most other files in iperf3 can get this @@ -71,33 +75,86 @@ */ extern int gerror; +int ctrl_wait_ms = 5000; + + +#ifdef __WIN32__ + +void nonblock(int s) { + unsigned long nb = 1; + if (ioctlsocket(s, FIONBIO, &nb) == SOCKET_ERROR) { + //VLOG_ERR(VLOG << "Error setting Connection FIONBIO: " + // << WSAGetLastError()); + } + else { + //VLOG << "Made socket: " << s << " non-blocking: " << msg << endl; + } +} +#else +void nonblock(int s) { + //VLOG_TRC(VLOG << "in other nonblock, msg: " << msg << endl); + if (fcntl(s, F_SETFL, O_NDELAY) == -1) { + //VLOG << "ERROR: fcntl (!LINUX), executing nonblock: " + // << LFSTRERROR << endl; + }//if +}//nonblock +#endif + +void print_fdset(int max_fd, fd_set* read_set, fd_set* write_set, struct iperf_test *test) { + int i; + iperf_err(test, "%llu read/write FD sets: ", (unsigned long long)getCurMs()); + for (i = 0; i<=max_fd; i++) { + if (FD_ISSET(i, read_set)) { + if (FD_ISSET(i, write_set)) { + fprintf(stdout, "%i RW ", i); + } + else { + fprintf(stdout, "%i RO ", i); + } + } + else if (FD_ISSET(i, write_set)) { + fprintf(stdout, "%i WO ", i); + } + } + fprintf(stdout, "\n"); +} + /* * timeout_connect adapted from netcat, via OpenBSD and FreeBSD * Copyright (c) 2001 Eric Jackson + * Now it assumes non-blocking socket passed in. */ int timeout_connect(int s, const struct sockaddr *name, socklen_t namelen, int timeout) { - struct pollfd pfd; socklen_t optlen; - int flags, optval; + int 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) { + if ((ret = connect(s, name, namelen)) != 0 && eWouldBlock()) { +#ifndef __WIN32__ + struct pollfd pfd; pfd.fd = s; pfd.events = POLLOUT; - if ((ret = poll(&pfd, 1, timeout)) == 1) { + if ((ret = poll(&pfd, 1, timeout)) == 1) +#else + fd_set write_fds; + FD_ZERO(&write_fds); //Zero out the file descriptor set + FD_SET(s, &write_fds); //Set the current socket file descriptor into the set + + //We are going to use select to wait for the socket to connect + struct timeval tv; //Time value struct declaration + tv.tv_sec = timeout / 1000; //The second portion of the struct + tv.tv_usec = (timeout % 1000) * 1000; //The microsecond portion of the struct + + int ret = select(s + 1, NULL, &write_fds, NULL, &tv); + if (ret == 1) +#endif + { optlen = sizeof(optval); if ((ret = getsockopt(s, SOL_SOCKET, SO_ERROR, - &optval, &optlen)) == 0) { + (char*)&optval, &optlen)) == 0) { errno = optval; ret = optval == 0 ? 0 : -1; } @@ -108,21 +165,20 @@ timeout_connect(int s, const struct sockaddr *name, socklen_t namelen, 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 -*/ + * Returns non-blocking socket. + */ /* make connection to server */ int -netdial(int domain, int proto, char *local, int local_port, char *server, int port, int timeout) +netdial(int domain, int proto, char *local, const char* bind_dev, int local_port, char *server, int port, int timeout, + struct iperf_test *test) { - struct addrinfo hints, *local_res, *server_res; + struct addrinfo hints, *local_res = NULL, *server_res = NULL; int s, saved_errno; if (local) { @@ -147,6 +203,28 @@ netdial(int domain, int proto, char *local, int local_port, char *server, int po return -1; } + setnonblocking(s, 1); + + if (test->debug) { + iperf_err(test, "netdial, domain: %d proto: %d local: %s bind-dev: %s local-port: %d server: %s:%d timeout: %d, socket: %d\n", + domain, proto, local, bind_dev, local_port, server, port, timeout, s); + } + + if (bind_dev) { +#ifdef SO_BINDTODEVICE + if (setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE, + bind_dev, IFNAMSIZ) < 0) +#endif + { + saved_errno = errno; + iclosesocket(s, test); + 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) { @@ -157,7 +235,7 @@ netdial(int domain, int proto, char *local, int local_port, char *server, int po if (bind(s, (struct sockaddr *) local_res->ai_addr, local_res->ai_addrlen) < 0) { saved_errno = errno; - close(s); + iclosesocket(s, test); freeaddrinfo(local_res); freeaddrinfo(server_res); errno = saved_errno; @@ -194,7 +272,7 @@ netdial(int domain, int proto, char *local, int local_port, char *server, int po if (bind(s, (struct sockaddr *) &lcl, addrlen) < 0) { saved_errno = errno; - close(s); + iclosesocket(s, test); freeaddrinfo(server_res); errno = saved_errno; return -1; @@ -202,9 +280,9 @@ netdial(int domain, int proto, char *local, int local_port, char *server, int po } ((struct sockaddr_in *) server_res->ai_addr)->sin_port = htons(port); - if (timeout_connect(s, (struct sockaddr *) server_res->ai_addr, server_res->ai_addrlen, timeout) < 0 && errno != EINPROGRESS) { + if (timeout_connect(s, (struct sockaddr *) server_res->ai_addr, server_res->ai_addrlen, timeout) < 0 && !eWouldBlock()) { saved_errno = errno; - close(s); + iclosesocket(s, test); freeaddrinfo(server_res); errno = saved_errno; return -1; @@ -217,7 +295,7 @@ netdial(int domain, int proto, char *local, int local_port, char *server, int po /***************************************************************/ int -netannounce(int domain, int proto, char *local, int port) +netannounce(int domain, int proto, char *local, const char* bind_dev, int port, struct iperf_test *test) { struct addrinfo hints, *res; char portstr[6]; @@ -225,6 +303,7 @@ netannounce(int domain, int proto, char *local, int port) 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 @@ -249,16 +328,36 @@ netannounce(int domain, int proto, char *local, int port) return -1; s = socket(res->ai_family, proto, 0); + + if (test->debug) { + iperf_err(test, "netannounce, domain: %d proto: %d local: %s bind-dev: %s port: %d fd: %d\n", + domain, proto, local, bind_dev, port, s); + } + if (s < 0) { freeaddrinfo(res); return -1; } + if (bind_dev) { +#ifdef SO_BINDTODEVICE + if (setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE, + bind_dev, IFNAMSIZ) < 0) +#endif + { + saved_errno = errno; + iclosesocket(s, test); + 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); + iclosesocket(s, test); freeaddrinfo(res); errno = saved_errno; return -1; @@ -280,7 +379,7 @@ netannounce(int domain, int proto, char *local, int port) if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, (char *) &opt, sizeof(opt)) < 0) { saved_errno = errno; - close(s); + iclosesocket(s, test); freeaddrinfo(res); errno = saved_errno; return -1; @@ -290,7 +389,7 @@ netannounce(int domain, int proto, char *local, int port) if (bind(s, (struct sockaddr *) res->ai_addr, res->ai_addrlen) < 0) { saved_errno = errno; - close(s); + iclosesocket(s, test); freeaddrinfo(res); errno = saved_errno; return -1; @@ -301,7 +400,7 @@ netannounce(int domain, int proto, char *local, int port) if (proto == SOCK_STREAM) { if (listen(s, INT_MAX) < 0) { saved_errno = errno; - close(s); + iclosesocket(s, test); errno = saved_errno; return -1; } @@ -310,30 +409,189 @@ netannounce(int domain, int proto, char *local, int port) return s; } +void iclosesocket(int s, struct iperf_test *test) { + int rv; + + if (s < 0) + return; + + if (test->debug) { + iperf_err(test, "Closing socket: %d", s); + } + +#ifdef __WIN32__ + rv = closesocket(s); +#else + rv = close(s); +#endif + + if (rv < 0) { + iperf_err(test, "Error closing socket %d, rv: %d, error: %s", + s, rv, STRERROR); + } + + if (s == test->ctrl_sck) + test->ctrl_sck = -1; + if (s == test->listener) + test->listener = -1; + if (s == test->prot_listener) + test->prot_listener = -1; + IFD_CLR(s, &test->read_set, test); + IFD_CLR(s, &test->write_set, test); +} + + +int waitRead(int fd, char *buf, size_t count, int prot, struct iperf_test *test, int timeout_ms) +{ + int sofar = 0; + uint64_t timeout_at = getCurMs() + timeout_ms; + fd_set read_fds; + struct timeval tv; + uint64_t now, sleep_for; + int select_ret; + + while (1) { + int r; + + if (test->debug > 1) + iperf_err(test, "waitRead, calling Nread, fd: %d count: %d sofar: %d", fd, (int)count, sofar); + r = Nread(fd, buf + sofar, count - sofar, prot, test); + if (test->debug > 1) + iperf_err(test, "waitRead, Nread, fd: %d count: %d sofar: %d rv: %d", + fd, (int)count, sofar, r); + if (r < 0) { + if (sofar == 0) + return r; + return sofar; + } + sofar += r; + if (sofar == count) + return sofar; + now = getCurMs(); + if (now >= timeout_at) + return sofar; + + /* not done, call select with timout so we don't busy-spin */ + sleep_for = timeout_at - now; + + FD_ZERO(&read_fds); + FD_SET(fd, &read_fds); + + tv.tv_sec = sleep_for / 1000; + tv.tv_usec = (sleep_for % 1000) * 1000; + + select_ret = select(fd + 1, &read_fds, NULL, NULL, &tv); + if (test->debug > 1) + iperf_err(test, "waitRead, done with select, fd: %d count: %d sofar: %d select-ret: %d", + fd, (int)count, sofar, select_ret); + if (select_ret <= 0) + return sofar; + } +} + +int waitSocketReadable(int fd, int wait_for_ms) { + fd_set read_fds; + struct timeval tv; + + FD_ZERO(&read_fds); + FD_SET(fd, &read_fds); + + tv.tv_sec = wait_for_ms / 1000; + tv.tv_usec = (wait_for_ms % 1000) * 1000; + + return select(fd + 1, &read_fds, NULL, NULL, &tv); +} + +int waitWrite(int fd, char *buf, size_t count, int prot, struct iperf_test *test, int timeout_ms) +{ + int sofar = 0; + uint64_t timeout_at = getCurMs() + timeout_ms; + fd_set write_fds; + struct timeval tv; + uint64_t now, sleep_for; + int select_ret; + + while (1) { + int r = Nwrite(fd, buf + sofar, count - sofar, prot, test); + if (r < 0) { + if (sofar == 0) + return r; + return sofar; + } + sofar += r; + if (sofar == count) + return sofar; + now = getCurMs(); + if (now >= timeout_at) + return sofar; + + /* not done, call select with timout so we don't busy-spin */ + sleep_for = timeout_at - now; + + FD_ZERO(&write_fds); + FD_SET(fd, &write_fds); + + tv.tv_sec = sleep_for / 1000; + tv.tv_usec = (sleep_for % 1000) * 1000; + + select_ret = select(fd + 1, NULL, &write_fds, NULL, &tv); + if (select_ret <= 0) + return sofar; + } +} + +int eWouldBlock() { +#ifndef __WIN32__ + return (errno == EINPROGRESS || errno == EAGAIN); +#else + return WSAGetLastError() == WSAEWOULDBLOCK; +#endif +} /*******************************************************************/ -/* reads 'count' bytes from a socket */ +/* reads up to 'count' bytes from a socket */ /********************************************************************/ int -Nread(int fd, char *buf, size_t count, int prot) +Nread(int fd, char *buf, size_t count, int prot, struct iperf_test *test) { + char* oldbuf = buf; register ssize_t r; register size_t nleft = count; while (nleft > 0) { + errno = 0; +#ifndef __WIN32__ r = read(fd, buf, nleft); +#else + r = recv(fd, buf, nleft, 0); +#endif if (r < 0) { - if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) + if (eWouldBlock() || errno == EINTR) { break; - else + } + else { + iperf_err(test, "Error in Nread (%s) fd: %d\n", + STRERROR, fd); return NET_HARDERROR; - } else if (r == 0) - break; + } + } else if (r == 0) { + // End of socket has happened + if (buf != oldbuf) { + // We read something first, though, report it as successful read + break; + } + else { + return NET_HANGUP; + } + } nleft -= r; buf += r; } + if (test && test->debug > 1) { + iperf_err(test, "Nread:\n%s", hexdump((const unsigned char*)oldbuf, count - nleft, 1, 1)); + } return count - nleft; } @@ -343,13 +601,22 @@ Nread(int fd, char *buf, size_t count, int prot) */ int -Nwrite(int fd, const char *buf, size_t count, int prot) +Nwrite(int fd, const char *buf, size_t count, int prot, struct iperf_test *test) { register ssize_t r; register size_t nleft = count; + if (test && test->debug > 1) { + iperf_err(test, "Nwrite:\n%s", hexdump((const unsigned char*)buf, count, 1, 1)); + } + while (nleft > 0) { + errno = 0; +#ifndef __WIN32__ r = write(fd, buf, nleft); +#else + r = send(fd, buf, nleft, 0); +#endif if (r < 0) { switch (errno) { case EINTR: @@ -363,10 +630,18 @@ Nwrite(int fd, const char *buf, size_t count, int prot) return NET_SOFTERROR; default: - return NET_HARDERROR; +#ifdef __WIN32__ + if (WSAGetLastError() == WSAEWOULDBLOCK) + return count - nleft; +#endif + return NET_HARDERROR; } - } else if (r == 0) - return NET_SOFTERROR; + } else if (r == 0) { + if ((count - nleft) == 0) + return NET_SOFTERROR; + else + return (count - nleft); /* already wrote some */ + } nleft -= r; buf += r; } @@ -456,6 +731,14 @@ Nsendfile(int fromfd, int tofd, const char *buf, size_t count) int setnonblocking(int fd, int nonblocking) { +#ifdef __WIN32__ + if (nonblocking) { + nonblock(fd); + return 0; + } + else + return -1; /*not supported currently */ +#else int flags, newflags; flags = fcntl(fd, F_GETFL, 0); @@ -473,6 +756,7 @@ setnonblocking(int fd, int nonblocking) return -1; } return 0; +#endif } /****************************************************************************/ @@ -488,3 +772,116 @@ getsockdomain(int sock) } return ((struct sockaddr *) &sa)->sa_family; } + + +#ifdef __WIN32__ + +// From 'mairix', under LGPL evidently + +/* The mmap/munmap implementation was shamelessly stolen, with minimal + changes, from libgwc32, a Windows port of glibc. */ + +static DWORD granularity = 0; +static int isw9x = -1; + +void * +mmap (void *addr, size_t len, int prot, int flags, int fd, off_t offset) +{ + void *map = NULL; + char *gran_addr = addr; + HANDLE handle = INVALID_HANDLE_VALUE; + DWORD cfm_flags = 0, mvf_flags = 0, sysgran; + off_t gran_offset = offset, filelen = _filelength(fd); + off_t mmlen = len; + + if (!granularity) + { + SYSTEM_INFO si; + + GetSystemInfo (&si); + granularity = si.dwAllocationGranularity; + } + sysgran = granularity; + + switch (prot) { + case PROT_READ | PROT_WRITE | PROT_EXEC: + case PROT_WRITE | PROT_EXEC: + cfm_flags = PAGE_EXECUTE_READWRITE; + mvf_flags = FILE_MAP_ALL_ACCESS; + break; + case PROT_READ | PROT_WRITE: + cfm_flags = PAGE_READWRITE; + mvf_flags = FILE_MAP_ALL_ACCESS; + break; + case PROT_WRITE: + cfm_flags = PAGE_READWRITE; + mvf_flags = FILE_MAP_WRITE; + break; + case PROT_READ: + cfm_flags = PAGE_READONLY; + mvf_flags = FILE_MAP_READ; + break; + case PROT_NONE: + cfm_flags = PAGE_NOACCESS; + mvf_flags = FILE_MAP_READ; + break; + case PROT_EXEC: + cfm_flags = PAGE_EXECUTE; + mvf_flags = FILE_MAP_READ; + break; + } + if (flags & MAP_PRIVATE) + { + if (isw9x == -1) + isw9x = ((DWORD)(LOBYTE (LOWORD (GetVersion()))) < 5); + if (isw9x == 1) + cfm_flags = PAGE_WRITECOPY; + mvf_flags = FILE_MAP_COPY; + } + if (flags & MAP_FIXED) + { + gran_offset = offset; + gran_addr = addr; + } + else + { + gran_offset = offset & ~(sysgran - 1); + gran_addr = (char *) (((DWORD) gran_addr / sysgran) * sysgran); + } + mmlen = (filelen < gran_offset + len ? filelen - gran_offset : len); + + handle = CreateFileMapping ((HANDLE) _get_osfhandle(fd), NULL, cfm_flags, + 0, mmlen, NULL); + if (!handle) + { + errno = EINVAL; /* FIXME */ + return MAP_FAILED; + } + map = MapViewOfFileEx (handle, mvf_flags, HIDWORD(gran_offset), + LODWORD(gran_offset), (SIZE_T) mmlen, + (LPVOID) gran_addr); + if (map == NULL && (flags & MAP_FIXED)) + { + map = MapViewOfFileEx (handle, mvf_flags, HIDWORD(gran_offset), + LODWORD(gran_offset), (SIZE_T) mmlen, + (LPVOID) NULL); + } + CloseHandle(handle); + + if (map == NULL) + { + errno = EINVAL; /* FIXME */ + return MAP_FAILED; + } + return map; +} + +int munmap (void *addr, size_t len) +{ + if (!UnmapViewOfFile (addr)) + return -1; + return 0; +} + +/* End of ISC stuff */ +#endif diff --git a/src/net.h b/src/net.h index 3738d6ae8..7749d4286 100644 --- a/src/net.h +++ b/src/net.h @@ -27,11 +27,26 @@ #ifndef __NET_H #define __NET_H +#include "iperf.h" + +extern int ctrl_wait_ms; + +int eWouldBlock(); + +void iclosesocket(int s, struct iperf_test *test); +void nonblock(int s); + +void print_fdset(int max_fd, fd_set* read_set, fd_set* write_set, struct iperf_test *test); + int timeout_connect(int s, const struct sockaddr *name, socklen_t namelen, int timeout); -int netdial(int domain, int proto, char *local, int local_port, char *server, int port, int timeout); -int netannounce(int domain, int proto, char *local, int port); -int 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 netdial(int domain, int proto, char *local, const char* bind_dev, int local_port, char *server, int port, int timeout, + struct iperf_test *test); +int netannounce(int domain, int proto, char *local, const char* bind_dev, int port, struct iperf_test *test); +int waitSocketReadable(int fd, int wait_for_ms); +int waitRead(int fd, char *buf, size_t count, int prot, struct iperf_test *test, int timeout_ms); +int Nread(int fd, char *buf, size_t count, int prot, struct iperf_test *test); +int waitWrite(int fd, char *buf, size_t count, int prot, struct iperf_test *test, int timeout_ms); +int Nwrite(int fd, const char *buf, size_t count, int prot, struct iperf_test *test) /* __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); @@ -40,5 +55,6 @@ int parse_qos(const char *tos); #define NET_SOFTERROR -1 #define NET_HARDERROR -2 +#define NET_HANGUP -3 /* socket has been closed */ #endif /* __NET_H */ diff --git a/src/t_units.c b/src/t_units.c index 8fd8bd92e..e11dc9a8c 100644 --- a/src/t_units.c +++ b/src/t_units.c @@ -73,37 +73,37 @@ main(int argc, char **argv) llu = (iperf_size_t) d; assert(llu == unit_atoi("3t")); - unit_snprintf(s, 11, 1024.0, 'A'); + unit_snprintf(s, 11, 1024.0, 'A', -1); assert(strncmp(s, "1.00 KByte", 11) == 0); - unit_snprintf(s, 11, 1024.0 * 1024.0, 'A'); + unit_snprintf(s, 11, 1024.0 * 1024.0, 'A', -1); assert(strncmp(s, "1.00 MByte", 11) == 0); - unit_snprintf(s, 11, 1000.0, 'k'); + unit_snprintf(s, 11, 1000.0, 'k', -1); assert(strncmp(s, "8.00 Kbit", 11) == 0); - unit_snprintf(s, 11, 1000.0 * 1000.0, 'a'); + unit_snprintf(s, 11, 1000.0 * 1000.0, 'a', -1); assert(strncmp(s, "8.00 Mbit", 11) == 0); d = 4.0 * 1024 * 1024 * 1024; - unit_snprintf(s, 11, d, 'A'); + unit_snprintf(s, 11, d, 'A', -1); assert(strncmp(s, "4.00 GByte", 11) == 0); - unit_snprintf(s, 11, d, 'a'); + unit_snprintf(s, 11, d, 'a', -1); assert(strncmp(s, "34.4 Gbit", 11) == 0); d = 4.0 * 1024 * 1024 * 1024 * 1024; - unit_snprintf(s, 11, d, 'A'); + unit_snprintf(s, 11, d, 'A', -1); assert(strncmp(s, "4.00 TByte", 11) == 0); - unit_snprintf(s, 11, d, 'a'); + unit_snprintf(s, 11, d, 'a', -1); assert(strncmp(s, "35.2 Tbit", 11) == 0); d = 4.0 * 1024 * 1024 * 1024 * 1024 * 1024; - unit_snprintf(s, 11, d, 'A'); + unit_snprintf(s, 11, d, 'A', -1); assert(strncmp(s, "4096 TByte", 11) == 0); - unit_snprintf(s, 11, d, 'a'); + unit_snprintf(s, 11, d, 'a', -1); assert(strncmp(s, "36029 Tbit", 11) == 0); return 0; diff --git a/src/tcp_info.c b/src/tcp_info.c index bfbb9ea4c..7d3f51a72 100644 --- a/src/tcp_info.c +++ b/src/tcp_info.c @@ -47,9 +47,11 @@ #include #include #include +#ifndef __WIN32__ #include -#include #include +#endif +#include #include #include "iperf.h" diff --git a/src/timer.c b/src/timer.c index 33923c79c..b30875b63 100644 --- a/src/timer.c +++ b/src/timer.c @@ -159,16 +159,27 @@ tmr_timeout( struct iperf_time* nowP ) static struct timeval timeout; getnow( nowP, &now ); + + /* For reasons I do not understand, the iperf server will sometimes hang in + * select and not accept new connections until some keyboard input happens + * (on Windows, at least). So, never block more than one second in select + * to try to mitigate this. --Ben + */ + timeout.tv_sec = 1; + timeout.tv_usec = 0; + /* 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; + if (timers) { + 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; + if (timeout.tv_sec > 1) + timeout.tv_sec = 1; + } return &timeout; } diff --git a/src/units.c b/src/units.c index 7376a0b7f..92c11cd00 100644 --- a/src/units.c +++ b/src/units.c @@ -1,3 +1,4 @@ + /*--------------------------------------------------------------- * Copyright (c) 1999,2000,2001,2002,2003 * The Board of Trustees of the University of Illinois @@ -57,7 +58,9 @@ #ifdef HAVE_STDINT_H #include #endif +#ifndef __WIN32__ #include +#endif #include #include @@ -263,14 +266,18 @@ extern "C" * adaptive picks the "best" one based on the number. * s should be at least 11 chars long * (4 digits + space + 5 chars max + null) + * If precision == -1, treat that as old behavior that tries to do 4 characters, + * otherwise, use it as decimial precision. * ------------------------------------------------------------------- */ void unit_snprintf(char *s, int inLen, - double inNum, char inFormat) + double inNum, char inFormat, int precision) { int conv; const char *suffix; const char *format; + char format_custom[40]; + int p = precision; /* convert to bits for [bkmga] */ if (!isupper((int) inFormat)) @@ -303,14 +310,30 @@ extern "C" if (isupper((int) inFormat)) { - while (tmpNum >= 1024.0 && conv < TERA_CONV) + double max = 1024.0; + int z; + for (z = 0; z 2 && precision < 2) + precision = 2; + while (tmpNum >= max && conv < TERA_CONV) { tmpNum /= 1024.0; conv++; } } else { - while (tmpNum >= 1000.0 && conv < TERA_CONV) + double max = 1000.0; + int z; + for (z = 0; z 2 && precision < 2) + precision = 2; + while (tmpNum >= max && conv < TERA_CONV) { tmpNum /= 1000.0; conv++; @@ -330,22 +353,28 @@ extern "C" 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 + if (precision == -1) { + /* 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";/* #### */ - } + format = "%4.0f %s";/* #### */ + } + } + else { + snprintf(format_custom, sizeof(format_custom), "%%0.%df %%s", precision); + format = format_custom; + } snprintf(s, inLen, format, inNum, suffix); } /* end unit_snprintf */ diff --git a/src/units.h b/src/units.h index 6ab921613..d37ea783f 100644 --- a/src/units.h +++ b/src/units.h @@ -25,10 +25,10 @@ * file for complete information. */ enum { - UNIT_LEN = 32 + UNIT_LEN = 64 }; 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 ); +void unit_snprintf( char *s, int inLen, double inNum, char inFormat, int precision );