/* * N U T T C P . C v8.1.2 * * Copyright(c) 2000 - 2015 Bill Fink. All rights reserved. * Copyright(c) 2003 - 2015 Rob Scott. All rights reserved. * * nuttcp is free, opensource software. You can redistribute it and/or * modify it under the terms of Version 2 of the GNU General Public * License (GPL), as published by the GNU Project (http://www.gnu.org). * A copy of the license can also be found in the LICENSE file. * * 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. * * Based on nttcp * Developed by Bill Fink, billfink@mindspring.com * and Rob Scott, rob@hpcmo.hpc.mil * Latest version available at: * http://nuttcp.net/nuttcp/beta/ * * Test TCP connection. Makes a connection on port 5000(ctl)/5101(data) * and transfers fabricated buffers or data copied from stdin. * * Run nuttcp with no arguments to get a usage statement * * Modified for operation under 4.2BSD, 18 Dec 84 * T.C. Slattery, USNA * Minor improvements, Mike Muuss and Terry Slattery, 16-Oct-85. * * 8.1.2, Bill Fink, 5-Sep-16 * Fix to not exercise 8.0.1 and 8.1.1 features for non-Linux systems * Changed nuttcp site to http://nuttcp.net/nuttcp/beta/ * Added define test for NOT_LINUX to undefine LINUX for build testing * 8.1.1, Bill Fink, 31-Aug-16 * Add "-Ris##" option to emulate smoothed slow start behavior (Linux only) * 8.0.1, Bill Fink, 22-Aug-16 * Add reporting of TCP congestion window * 7.3.4, Rob Scott & Bill Fink, 18-Jul-16 * Use clock_gettime() on Windows/CYGWIN and Linux (if available) * 7.3.3, Rob Scott, 1-May-15 * Fix tos to work for ipv6 by setting traffic class * 7.3.2, Bill Fink, 3-Aug-14 * Allow longer server info timeout for third party via --idle-data-timeout * 7.3.1, Bill Fink, 27-Jul-14 * Added feature to specify source port with "-p#:#" and "-P#:#" * Updated Copyright notice for new year * 7.2.2, Bill Fink, 25-May-13 * Fix Linux bug of exceeding MAX_EOT_WAIT_SEC on lossy large RTT path * with large window causing false "Error: receiver not ACKing data" * Change Linux method for draining socket send queue at EOT while waiting * for any TCP retransmissions to complete - instead of checking * tcpi_unacked value from TCP_INFO getsockopt() use SIOCOUTQ ioctl() * (new error message is "Error: timeout while draining socket send queue") * 7.2.1, Bill Fink, 26-Dec-12 * Add "-g" option to specify multicast IP address to use * Clean up really confused transmit code for IPv4/IPv6 SSM multicast * Bug fix from Aristeu Rozanski: * Crash caused by closing TCP_ADV_WIN_SCALE file even if open failed * 7.1.6, Bill Fink, 25-Feb-12 * Add "-sd" direct I/O option for non-sinkmode (Linux only) * Fix bug with server CPU affinity being parsed as %X instead of %d * For non-sinkmode insure complete network block is written to stdout * Above fixes nuttscp bug seen with --copy-dir getting premature EOF * Updated Copyright notice for new year * Whitespace style cleanups * 7.1.5, Rob Scott, 19-Jul-11 * Not every system has ERESTART added in 7.1.4, wrapped in ifdef * 7.1.4, Bill Fink, 30-May-11 * Updated Copyright notice * Use -DHAVE_SS_FAMILY to override _AIX workaround for newer AIX * AIX can get ERESTART rather than EINTR/EAGAIN error on interrupted I/O * Fix non-Linux systems to properly count TCP retrans for multiple streams * Allow xinetd nuttcp server to handle both IPv4 & IPv6 if no explicitaf * Detect EOD for non-sinkmode UDP transfers * Suppress bogus warning when using maximum size UDP packet * 7.1.3, Bill Fink, 1-Apr-10 * Fix compiler warnings generated by Ubuntu 9.x gcc 4.3.x/4.4.x versions * (to see warnings compile with -DWANT_WUR) * 7.1.2, Bill Fink, 23-Jan-10 * Updated Copyright notice * Terminate non-sinkmode after specified file size with "-n" option * Allow multilink aggregation with "-N##m" option to work for receive * Add "-sz" zero copy option for non-sinkmode when input is a regular file * Fix compiler warnings about strict-aliasing rules for variable group * Remove "-Sf" forced server mode from Usage: statement * Fix zeroing of clientaddr6 during server cleanup * Fix freeaddrinfo() processing during cleanup * Change manually started oneshot server to have parent process just exit * Some minor whitespace cleanup * 7.1.1, Bill Fink, 24-Dec-09 * Provide summary TCP retrans info for multi-stream TCP * Fix bug with retrans interval info when -fparse * Add "+stride" or "+n.n.n.n" syntax for multi-stream TCP (IPv4) * Fix third party bug with "-xc" option adding extraneous 't' character * Add optional client-side name resolution for third party host * Add "-N##m" option for multilink aggregation for multiple streams * Add "-xc#/#" and "-P#/#" options to Usage: statement * Some minor whitespace cleanup * 7.0.1, Bill Fink, 18-Sep-09 * Enable jitter measurements with "-j" option * Enable one-way delay measurements with "-o" option * Fix bug with RTT and -fparse * 6.2.10, Bill Fink, 1-Aug-09 * Change ctl/data port checks to < 1024 instead of < 5000 * Fix "--idle-data-timeout" Usage: statement for new default minimum * Improve transmit performance with "-i" by setting poll() timeout to 0 * Remove commented out code for unused normal_eod * Don't output interval retrans info if non-sinkmode (for nuttscp) * 6.2.9, Bill Fink, 24-Jun-09 * Make retrans info reporting work again on newer Linux distros * Skip check for unACKed data at end of transfer if -DBROKEN_UNACKED * 6.2.8, Bill Fink, 8-Jun-09 * Play nice with iperf (change default data port to 5101) * Delay sending of server "OK" until after successful server bind() * Client check for server errors before starting data transfer * Continue checking for server output while draining client transmission * Correct "server not ACKing data" error message (server -> receiver) * Add "--packet-burst" option for Rob * Fix "--idle-data-timeout" Usage: statement for client * Improve accuracy of retrans info timing synchronization (client xmitter) * Change reference to nuttcp repository from ftp:// to http:// * Fix bug affecting nuttscp introduced in 6.2.4 (not honoring oneshot) * Whitespace cleanup: get rid of , $, & $ * Whitespace cleanup: convert 8 spaces to where appropriate * 6.2.7, Bill Fink, 22-May-09 * Allow rate limit to be exceeded temporarily by n packets ("-Rixxx/n") * Fix several reqval parameter settings * 6.2.6, Bill Fink, 17-Apr-09 * Allow setting server CPU affinity from client via "-xcs" option * Allow setting client & server CPU affinity via third party * Fix bug with option processing when reqval is set * 6.2.5, Bill Fink, 16-Apr-09 * Allow passing of third party control port via "-Pctlport/ctlport3" * Up default idle data minimum to 15 sec to better handle net transients * Don't reset nstream until after last use (fix getaddrinfo() memory leak) * 6.2.4, Bill Fink, 10-Apr-09 * Fix bug with simultaneous server connections to manually started server * 6.2.3, Bill Fink, 5-Apr-09 * Add "-xc" option to set CPU affinity (Linux only) * Fix Usage: statement: "--idle-data-timeout" both server & client option * Don't reset priority on server cleanup * Fix priority output for "-fparse" * 6.2.2, Bill Fink, 3-Apr-09 * Fix bad third party bug causing >= 1 minute transfers to silently fail * Fix Usage: statement: "--idle-data-timeout" not just a server option * 6.2.1, Rob Scott, 22-Mar-09 * Added IPv6 and SSM MC support * Ported from Rob's 5.5.5 based code by Bill Fink * 6.1.5, Bill Fink, 5-Mar-09 * Fix client lockup with third party when network problem (for scripts) * 6.1.4, Bill Fink, 5-Jan-09 * Updated Copyright notice * Bugfix: set chk_idle_data on client (now also checks no data received) * Use argv[0] instead of "nuttcp" for third party * Bugfix: give error message again on error starting server * 6.1.3, Bill Fink, 17-Sep-08 * Timeout client accept() too and give nice error message (for scripts) * 6.1.2, Bill Fink, 29-Aug-08 * Don't wait forever for unacked data at EOT (limit to 1 minute max) * Extend no data received protection to client too (for scripts) * Give nice error messages to client for above cases * Don't hang getting server info if server exited (timeout reads) * 6.1.1, Bill Fink, 26-Aug-08 * Remove beta designation * Report RTT by default (use "-f-rtt" to suppress) * Moved RTT info to "connect to" line * Correct bogus IP frag warning for e.g. "-l1472" or "-l8972" * Don't report interval host-retrans if no data rcvd (e.g. initial PMTU) * Correct reporting of retrans info with "-fparse" option * Correct reporting of RTT with "-F" flip option * Report doubled send and receive window sizes (for Linux) * Add report of available send and receive window sizes (for Linux) * Touchup TODO list to remove some already completed items * 6.0.7, Bill Fink, 19-Aug-08 * Add delay (default 0.5 sec) to "-a" option & change max retries to 10 * Updated Copyright notice * 6.0.6, Bill Fink, 11-Aug-08 * Delay server forking until after listen() for better error status * Above suggested by Hans Blom (jblom@science.uva.nl) * Make forced server mode the default behavior * Check address family on getpeername() so "ssh host nuttcp -S" works * Add setsid() call for manually started server to create new session * Some minor code cleanup * 6.0.5, Bill Fink, 10-Aug-08 * Allow "-s" from/to stdin/stdout with "-1" oneshot server mode * Switch beta vers message to stderr to not interfere with "-s" option * Don't set default timeout if "-s" specified * Give error message for "-s" with "-S" (xinetd or manually started) * 6.0.4, Bill Fink, 18-Jul-08 * Forgot about 3rd party support for RTT info - fix that * 6.0.3, Bill Fink, 17-Jul-08 * A better (and accurate) way to get RTT info (and not just Linux) * 6.0.2, Bill Fink, 16-Jul-08 * Add RTT info to brief output for Linux * 6.0.1, Bill Fink, 17-Dec-07 * Add reporting of TCP retransmissions (interval reports on Linux TX only) * Add reporting of data transfer RTT for verbose Linux output * Add beta version messages and "-f-beta" option to suppress * Automatically switch "classic" mode to oneshot server mode * Fix UDP loss info bug not updating stream_idx when not need_swap * Fix compiler warning doing sprintf of timeout * Correct comment on NODROPS define * 5.5.5, Bill Fink, 1-Feb-07 * Change default MC addr to be based on client addr instead of xmit addr * 5.5.4, Bill Fink, 3-Nov-06 * Fix bug with negative loss causing huge drop counts on interval reports * Updated Copyright notice and added GPL license notice * 5.5.3, Bill Fink, 23-Oct-06 * Fix bug with "-Ri" instantaneous rate limiting not working properly * 5.5.2, Bill Fink, 25-Jul-06 * Make manually started server multi-threaded * Add "--single-threaded" server option to restore old behavior * Add "-a" client option to retry a failed server connection "again" * (for certain possibly transient errors) * 5.5.1, Bill Fink, 22-Jul-06 * Fix bugs with nbuf_bytes and rate_pps used with 3rd party * Pass "-D" option to server (and also make work for third party) * Allow setting precedence with "-c##p" * 5.4.3, Rob Scott & Bill Fink, 17-Jul-06 * Fix bug with buflen passed to server when no buflen option speicified * (revert 5.3.2: Fix bug with default UDP buflen for 3rd party) * Better way to fix bug with default UDP buflen for 3rd party * Trim trailing '\n' character from err() calls * Use fcntl() to set O_NONBLOCK instead of MSG_DONTWAIT to send() ABORT * (and remove MSG_DONTWAIT from recv() because it's not needed) * Don't re-initialize buflen at completion of server processing * (non inetd: is needed to check for buffer memory allocation change, * caused bug if smaller "-l" followed by larger default "-l") * 5.4.2, Bill Fink, 1-Jul-06 * Fix bug with interrupted UDP receive reporting negative packet loss * Make sure errors (or debug) from server are propagated to the client * Make setsockopt SO_SNDBUF/SO_RCVBUF error not be fatal to server * Don't send stderr to client if nofork is set (manually started server) * 5.4.1, Bill Fink, 30-Jun-06 * Fix bug with UDP reporting > linerate because of bad correction * Send 2nd UDP BOD packet in case 1st is lost, e.g. waiting for ARP reply * (makes UDP BOD more robust for new separate control and data paths) * Fix bug with interval reports after EOD for UDP with small interval * Don't just exit inetd server on no data so can get partial results * (keep an eye out that servers don't start hanging again) * Make default idle data timeout 1/2 of timeout if interval not set * (with a minimum of 5 seconds and a maximum of 60 seconds) * Make server send abort via urgent TCP data if no UDP data received * (non-interval only: so client won't keep transmitting for full period) * Workaround for Windows not handling TCP_MAXSEG getsockopt() * 5.3.4, Bill Fink, 21-Jun-06 * Add "--idle-data-timeout" server option * (server ends transfer if no data received for the specified * timeout interval, previously it was a fixed 60 second timeout) * Shutdown client control connection for writing at end of UDP transfer * (so server can cope better with loss of all EOD packets, which is * mostly of benefit when using separate control and data paths) * 5.3.3, Bill Fink & Mark S. Mathews, 18-Jun-06 * Add new capability for separate control and data paths * (using syntax: nuttcp ctl_name_or_IP/data_name_or_IP) * Extend new capability for multiple independent data paths * (using syntax: nuttcp ctl/data1/data2/.../datan) * Above only supported for transmit or flipped/reversed receive * Fix -Wall compiler warnings on 64-bit systems * Make manually started server also pass stderr to client * (so client will get warning messages from server) * 5.3.2, Bill Fink, 09-Jun-06 * Fix bug with default UDP buflen for 3rd party * Fix compiler warnings with -Wall on FreeBSD * Give warning that windows doesn't support TCP_MAXSEG * 5.3.1, Rob Scott, 06-Jun-06 * Add "-c" COS option for setting DSCP/TOS setting * Fix builds on latest MacOS X * Fix bug with 3rd party unlimited rate UDP not working * Change "-M" option to require a value * Fix compiler warnings with -Wall (thanks to Daniel J Blueman) * Remove 'v' from nuttcp version (simplify RPM packaging) * V5.2.2, Bill Fink, 13-May-06 * Have client report server warnings even if not verbose * V5.2.1, Bill Fink, 12-May-06 * Pass "-M" option to server so it also works for receives * Make "-uu" be a shortcut for "-u -Ru" * V5.1.14, Bill Fink, 11-May-06 * Fix cancellation of UDP receives to work properly * Allow easy building without IPv6 support * Set default UDP buflen to largest 2^n less than MSS of ctlconn * Add /usr/local/sbin and /usr/etc to path * Allow specifying rate in pps by using 'p' suffix * Give warning if actual send/receive window size is less than requested * Make UDP transfers have a default rate limit of 1 Mbps * Allow setting MSS for client transmitter TCP transfers with "-M" option * Give more precision on reporting small UDP percentage data loss * Disallow UDP transfers in "classic" mode * Notify when using "classic" mode * V5.1.13, Bill Fink, 8-Apr-06 * Make "-Ri" instantaneous rate limit for very high rates more accurate * (including compensating for microsecond gettimeofday() granularity) * Fix bug giving bogus time/stats on UDP transmit side with "-Ri" * Allow fractional rate limits (for 'm' and 'g' only) * V5.1.12, Bill Fink & Rob Scott, 4-Oct-05 * Terminate server receiver if client control connection goes away * or if no data received from client within CHECK_CLIENT_INTERVAL * V5.1.11, Rob Scott, 25-Jun-04 * Add support for scoped ipv6 addresses * V5.1.10, Bill Fink, 16-Jun-04 * Allow 'b' suffix on "-w" option to specify window size in bytes * V5.1.9, Bill Fink, 23-May-04 * Fix bug with client error on "-d" option putting server into bad state * Set server accept timeout (currently 5 seconds) to prevent stuck server * Add nuttcp version info to error message from err() exit * V5.1.8, Bill Fink, 22-May-04 * Allow 'd|D' suffix to "-T" option to specify days * Fix compiler warning about unused variable cp in getoptvalp routine * Interval value check against timeout value should be >= * V5.1.7, Bill Fink, 29-Apr-04 * Drop "-nb" option in favor of "-n###[k|m|g|t|p]" * V5.1.6, Bill Fink, 25-Apr-04 * Fix bug with using interval option without timeout * V5.1.5, Bill Fink, 23-Apr-04 * Modification to allow space between option parameter and its value * Permit 'k' or 'm' suffix on "-l" option * Add "-nb" option to specify number of bytes to transfer * Permit 'k', 'm', 'g', 't', or 'p' suffix on "-n" and "-nb" options * V5.1.4, Bill Fink, 21-Apr-04 * Change usage statement to use standard out instead of standard error * Fix bug with interval > timeout, give warning and ignore interval * Fix bug with counting error value in nbytes on interrupted transfers * Fix bug with TCP transmitted & received nbytes not matching * Merge "-t" and "-r" options in Usage: statement * V5.1.3, Bill Fink, 9-Apr-04 * Add "-Sf" force server mode (useful for starting server via rsh/ssh) * Allow non-root user to find nuttcp binary in "." * Fix bug with receives terminating early with manual server mode * Fix bug with UDP receives not terminating with "-Ri" option * Clean up output formatting of nbuf (from "%d" to "%llu") * Add "-SP" to have 3rd party use same outgoing control port as incoming * V5.1.2, Bill Fink & Rob Scott, 18-Mar-04 * Fix bug with nbuf wrapping on really long transfers (int -> uint64_t) * Fix multicast address to be unsigned long to allow shift * Add MacOS uint8_t definition for new use of uint8_t * V5.1.1, Bill Fink, 8-Nov-03 * Add IPv4 multicast support * Delay receiver EOD until EOD1 (for out of order last data packet) * Above also drains UDP receive buffer (wait for fragmentation reassembly) * V5.0.4, Bill Fink, 6-Nov-03 * Fix bug reporting 0 drops when negative loss percentage * V5.0.3, Bill Fink, 6-Nov-03 * Kill server transmission if control connection goes away * Kill 3rd party nuttcp if control connection goes away * V5.0.2, Bill Fink, 4-Nov-03 * Fix bug: some dummy wasn't big enough :-) * V5.0.1, Bill Fink, 3-Nov-03 * Add third party support * Correct usage statement for "-xt" traceroute option * Improved error messages on failed options requiring client/server mode * V4.1.1, David Lapsley and Bill Fink, 24-Oct-03 * Added "-fparse" format option to generate key=value parsable output * Fix bug: need to open data connection on abortconn to clear listen * V4.0.3, Rob Scott, 13-Oct-03 * Minor tweaks to output format for alignment * Interval option "-i" with no explicit value sets interval to 1.0 * V4.0.2, Bill Fink, 10-Oct-03 * Changed "-xt" option to do both forward and reverse traceroute * Changed to use brief output by default ("-v" for old default behavior) * V4.0.1, Rob Scott, 10-Oct-03 * Added IPv6 code * Changed inet get functions to protocol independent versions * Added fakepoll for hosts without poll() (macosx) * Added ifdefs to only include setprio if os supports it (non-win) * Added bits to handle systems without new inet functions (solaris < 2.8) * Removed SYSV obsolete code * Added ifdefs and code to handle cygwin and beginning of windows support * Window size can now be in meg (m|M) and gig (g|G) * Added additional directories to search for traceroute * Changed default to transmit, time limit of 10 seconds, no buffer limit * Added (h|H) as option to specify time in hours * Added getservbyname calls for port, if all fails use old defaults * Changed sockaddr to sockaddr_storage to handle v6 addresses * v3.7.1, Bill Fink, 10-Aug-03 * Add "-fdebugpoll" option to help debug polling for interval reporting * Fix Solaris compiler warning * Use poll instead of separate process for interval reports * v3.6.2, Rob Scott, 18-Mar-03 * Allow setting server window to use default value * Cleaned out BSD42 old code * Marked SYSV code for future removal as it no longer appears necessary * Also set RCVBUF/SNDBUF for udp transfers * Changed transmit SO_DEBUG code to be like receive * Some code rearrangement for setting options before accept/connect * v3.6.1, Bill Fink, 1-Mar-03 * Add -xP nuttcp process priority option * Add instantaneous rate limit capability ("-Ri") * Don't open data connection if server error or doing traceroute * Better cleanup on server connection error (close open data connections) * Don't give normal nuttcp output if server error requiring abort * Implement -xt traceroute option * v3.5.1, Bill Fink, 27-Feb-03 * Don't allow flip option to be used with UDP * Fix bug with UDP and transmit interval option (set stdin unbuffered) * Fix start of UDP timing to be when get BOD * Fix UDP timing when don't get first EOD * Fix ident option used with interval option * Add "-f-percentloss" option to not give %loss info on brief output * Add "-f-drops" option to not give packet drop info on brief output * Add packet drop info to UDP brief output (interval report and final) * Add "-frunningtotal" option to give cumulative stats for "-i" * Add "-fdebuginterval" option to help debug interval reporting * Add "-fxmitstats" option to give transmitter stats * Change flip option from "-f" to "-F" * Fix divide by zero bug with "-i" option and very low rate limit * Fix to allow compiling with Irix native compiler * Fix by Rob Scott to allow compiling on MacOS X * v3.4.5, Bill Fink, 29-Jan-03 * Fix client/server endian issues with UDP loss info for interval option * v3.4.4, Bill Fink, 29-Jan-03 * Remove some debug printout for interval option * Fix bug when using interval option reporting 0.000 MB on final * v3.4.3, Bill Fink, 24-Jan-03 * Added UDP approximate loss info for interval reporting * Changed nbytes and pbytes from double to uint64_t * Changed SIGUSR1 to SIGTERM to kill sleeping child when done * v3.4.2, Bill Fink, 15-Jan-03 * Make work right with receive too * v3.4.1, Bill Fink, 13-Jan-03 * Fix bug interacting with old servers * Add "-f" flip option to reverse direction of data connection open * Fix bug by disabling interval timer when server done * v3.3.2, Bill Fink, 11-Jan-03 * Make "-i" option work for client transmit too * Fix bug which forced "-i" option to be at least 0.1 seconds * v3.3.1, Bill Fink, 7-Jan-03 * Added -i option to set interval timer (client receive only) * Fixed server bug not setting socket address family * v3.2.1, Bill Fink, 25-Feb-02 * Fixed bug so second will definitely kill nuttcp * Changed default control port to 5000 (instead of data port - 1) * Modified -T option to accept fractional seconds * v3.1.10, Bill Fink, 6-Feb-02 * Added -I option to identify nuttcp output * Made server always verbose (filtering is done by client) * Update to usage statement * Minor fix to "-b" output when "-D" option is used * Fix bug with "-s" that appends nuttcp output to receiver data file * Fix bug with "-b" that gave bogus CPU utilization on > 1 hour transfers * v3.1.9, Bill Fink, 21-Dec-01 * Fix bug with "-b" option on SGI systems reporting 0% CPU utilization * v3.1.8, Bill Fink, 21-Dec-01 * Minor change to brief output format to make it simpler to awk * v3.1.7, Bill Fink, 20-Dec-01 * Implement "-b" option for brief output (old "-b" -> "-wb") * Report udp loss percentage when using client/server mode * Fix bug with nbytes on transmitter using timed transfer * Combined send/receive window size printout onto a single line * v3.1.6, Bill Fink, 11-Jun-01 * Fixed minor bug reporting error connecting to inetd server * Previously, Bill Fink, 7-Jun-01 * Added -h (usage) and -V (version) options * Fixed SGI compilation warnings * Added reporting server version to client * Added version info and changed ttcp prints to nuttcp * Fixed bug with inetd server and client using -r option * Added ability to run server from inetd * Added udp capability to server option * Added -T option to set timeout interval * Added -ws option to set server window * Added -S option to support running receiver as daemon * Allow setting UDP buflen up to MAXUDPBUFLEN * Provide -b option for braindead Solaris 2.8 * Added printing of transmit rate limit * Added -w option to usage statement * Added -N option to support multiple streams * Added -R transmit rate limit option * Fix setting of destination IP address on 64-bit Irix systems * Only set window size in appropriate direction to save memory * Fix throughput calculation for large transfers (>= 2 GB) * Fix reporting of Mb/s to give actual millions of bits per second * Fix setting of INET address family in local socket * Fix setting of receiver window size * * TODO/Wish-List: * Transmit interval marking option * Allow at least some traceroute options * Add "-ut" option to do both UDP and TCP simultaneously * Default rate limit UDP if too much loss * Ping option * Other brief output formats * Linux window size bug/feature note * Network interface interrupts (for Linux only) * netstat -i info * Man page * Forking for multiple streams * Bidirectional option * Graphical interface * MTU info * Warning for window size limiting throughput * Auto window size optimization * Transmitter profile and playback options * Server side limitations (per client host/network) * Server side logging * Client/server security (password) * nuttcp server registration * nuttcp proxy support (firewalls) * nuttcp network idle time * * Distribution Status - * OpenSource(tm) * Licensed under version 2 of the GNU GPL * Please send source modifications back to the authors * Derivative works should be redistributed with a modified * source and executable name */ /* #ifndef lint static char RCSid[] = "@(#)$Revision: 1.2 $ (BRL)"; #endif */ #ifdef NOT_LINUX #undef linux #endif #ifndef WANT_WUR #undef _FORTIFY_SOURCE #else #define _FORTIFY_SOURCE 2 #endif #define _FILE_OFFSET_BITS 64 #if defined(linux) #define _GNU_SOURCE #endif #include #include #include #include #include #include /* struct timeval */ #include #ifndef _WIN32 #include #include #include #include #include #include #else #include "win32nuttcp.h" /* from win32 */ #endif /* _WIN32 */ #include #include #include #if defined(linux) #include #include #include #include #endif /* Let's try changing the previous unwieldy check */ /* #if defined(linux) || defined(__FreeBSD__) || defined (sgi) || (defined(__MACH__) && defined(_SOCKLEN_T)) || defined(sparc) || defined(__CYGWIN__) */ /* to the following (hopefully equivalent) simpler form like we use * for HAVE_POLL */ #if !defined(_WIN32) && (!defined(__MACH__) || defined(_SOCKLEN_T)) #include #include #include #endif #ifndef USE_GETTIMEOFDAY #ifndef HAVE_CLOCK_GETTIME #if (defined(linux) && defined(_POSIX_TIMERS) && (_POSIX_TIMERS > 0) && (__GLIBC__ >= 2) && (__GLIBC_MINOR__ >= 17)) || defined(__CYGWIN__) #define HAVE_CLOCK_GETTIME #endif #endif #endif #ifdef HAVE_CLOCK_GETTIME #include /* clock_gettime */ #endif #ifndef ULLONG_MAX #define ULLONG_MAX 18446744073709551615ULL #endif #define MAXRATE 0xffffffffUL #if !defined(__CYGWIN__) && !defined(_WIN32) #define HAVE_SETPRIO #endif #if defined(linux) #define HAVE_SETAFFINITY #endif #if !defined(_WIN32) && (!defined(__MACH__) || defined(_SOCKLEN_T)) #define HAVE_POLL #endif #if defined(__APPLE__) && defined(__MACH__) #define uint64_t u_int64_t #define uint32_t u_int32_t #define uint16_t u_int16_t #define uint8_t u_int8_t #endif #ifdef HAVE_POLL #include #else #include "fakepoll.h" /* from missing */ #endif #ifdef HAVE_SETAFFINITY #include #endif #ifndef CPU_SETSIZE #undef HAVE_SETAFFINITY #endif /* * _SOCKLEN_T is now defined by apple when they typedef socklen_t * * EAI_NONAME has nothing to do with socklen, but on sparc without it tells * us it's an old enough solaris to need the typedef */ #if (defined(__APPLE__) && defined(__MACH__)) && !defined(_SOCKLEN_T) || (defined(sparc) && !defined(EAI_NONAME)) typedef int socklen_t; #endif #if defined(sparc) && !defined(EAI_NONAME) /* old sparc */ #define sockaddr_storage sockaddr #define ss_family sa_family #endif /* old sparc */ #if defined(_AIX) && !defined(HAVE_SS_FAMILY) #define ss_family __ss_family #endif #if !defined(EAI_NONAME) #include "addrinfo.h" /* from missing */ #endif #define BETA_STR "-beta8" #define BETA_FEATURES "jitter/owd" union sockaddr_union { struct sockaddr_storage ss; struct sockaddr_in sin; struct sockaddr_in6 sin6; }; static struct timeval time0; /* Time at which timing started */ static struct timeval timepk; /* Time at which last packet sent */ static struct timeval timepkr; /* Time at which last packet received */ static struct timeval timepkri; /* timepkr for interval reports */ static struct timeval timep; /* Previous time - for interval reporting */ static struct timeval timetx; /* Transmitter timestamp */ static struct timeval timerx; /* Receive timestamp */ static struct rusage ru0; /* Resource utilization at the start */ static struct sigaction sigact; /* signal handler for alarm */ static struct sigaction savesigact; #define PERF_FMT_OUT "%.4f MB in %.2f real seconds = %.2f KB/sec" \ " = %.4f Mbps\n" #define PERF_FMT_BRIEF "%10.4f MB / %6.2f sec = %9.4f Mbps %d %%TX %d %%RX" #define PERF_FMT_BRIEF2 "%10.4f MB / %6.2f sec = %9.4f Mbps %d %%%s" #define PERF_FMT_BRIEF3 " Trans: %.4f MB" #define PERF_FMT_INTERVAL "%10.4f MB / %6.2f sec = %9.4f Mbps" #define PERF_FMT_INTERVAL2 " Tot: %10.4f MB / %6.2f sec = %9.4f Mbps" #define PERF_FMT_INTERVAL3 " Trans: %10.4f MB" #define PERF_FMT_INTERVAL4 " Tot: %10.4f MB" #define PERF_FMT_IN "%lf MB in %lf real seconds = %lf KB/sec = %lf Mbps\n" #define CPU_STATS_FMT_IN "%*fuser %*fsys %*d:%*dreal %d%%" #define CPU_STATS_FMT_IN2 "%*fuser %*fsys %*d:%*d:%*dreal %d%%" #define LOSS_FMT " %.2f%% data loss" #define LOSS_FMT_BRIEF " %.2f %%loss" #define LOSS_FMT_INTERVAL " %5.2f ~%%loss" #define LOSS_FMT5 " %.5f%% data loss" #define LOSS_FMT_BRIEF5 " %.5f %%loss" #define LOSS_FMT_INTERVAL5 " %7.5f ~%%loss" #define DROP_FMT " %lld / %lld drop/pkt" #define DROP_FMT_BRIEF " %lld / %lld drop/pkt" #define DROP_FMT_INTERVAL " %5lld / %5lld ~drop/pkt" #define JITTER_MIN 1 #define JITTER_AVG 2 #define JITTER_MAX 4 #define JITTER_IGNORE_OOO 8 #define JITTER_FMT "min-jitter = %.4f ms, avg-jitter = %.4f ms, " \ "max-jitter = %.4f ms" #define JITTER_MIN_FMT_BRIEF " %.4f msMinJitter" #define JITTER_AVG_FMT_BRIEF " %.4f msAvgJitter" #define JITTER_MAX_FMT_BRIEF " %.4f msMaxJitter" #define JITTER_MIN_FMT_INTERVAL " %.4f msMinJitter" #define JITTER_AVG_FMT_INTERVAL " %.4f msAvgJitter" #define JITTER_MAX_FMT_INTERVAL " %.4f msMaxJitter" #define JITTER_FMT_IN "jitter = %lf ms, avg-jitter = %lf ms, " \ "max-jitter = %lf ms" #define OWD_MIN 1 #define OWD_AVG 2 #define OWD_MAX 4 #define OWD_FMT "min-OWD = %.4f ms, avg-OWD = %.4f ms, " \ "max-OWD = %.4f ms" #define OWD_MIN_FMT_BRIEF " %.4f msMinOWD" #define OWD_AVG_FMT_BRIEF " %.4f msAvgOWD" #define OWD_MAX_FMT_BRIEF " %.4f msMaxOWD" #define OWD_MIN_FMT_INTERVAL " %.4f msMinOWD" #define OWD_AVG_FMT_INTERVAL " %.4f msAvgOWD" #define OWD_MAX_FMT_INTERVAL " %.4f msMaxOWD" #define OWD_FMT_IN "OWD = %lf ms, avg-OWD = %lf ms, max-OWD = %lf ms" #define RETRANS_FMT "%sretrans = %d" #define RETRANS_FMT_BRIEF " %d %sretrans" #define RETRANS_FMT_BRIEF_STR1 " %d = %d" #define RETRANS_FMT_BRIEF_STR2 " retrans" #define RETRANS_FMT_INTERVAL " %5d %sretrans" #define RETRANS_FMT_IN "retrans = %d" #define CWND_FMT " cwnd = %d KB" #define CWND_FMT_BRIEF " %d KB-cwnd" #define CWND_FMT_BRIEF_STR1 " %d = %d" #define CWND_FMT_BRIEF_STR2 " KB-cwnd" #define CWND_FMT_INTERVAL " %6d KB-cwnd" #define CWND_FMT_IN "cwnd = %d" #define RTT_FMT " RTT=%.3f ms" #define RTT_FMT_BRIEF " %.2f msRTT" #define RTT_FMT_IN "RTT=%lf" #define RTT_FMT_INB "RTT = %lf" #define SIZEOF_TCP_INFO_RETRANS 104 /* define NEW_TCP_INFO if struct tcp_info in /usr/include/netinet/tcp.h * contains tcpi_total_retrans member * * tcpi_rcv_rtt, tcpi_rcv_space, & tcpi_total_retrans were added * in glibc-headers-2.7 (Fedora 8) which fortunately also defined * TCP_MD5SIG at the same time, so key off of that */ #if defined(linux) && defined(TCP_MD5SIG) #define NEW_TCP_INFO #endif #ifndef NEW_TCP_INFO #define OLD_TCP_INFO #endif /* Parsable output formats */ #define P_PERF_FMT_OUT "megabytes=%.4f real_seconds=%.2f " \ "rate_KBps=%.2f rate_Mbps=%.4f\n" #define P_PERF_FMT_BRIEF "megabytes=%.4f real_seconds=%.2f rate_Mbps=%.4f " \ "tx_cpu=%d rx_cpu=%d" #define P_PERF_FMT_BRIEF3 " tx_megabytes=%.4f" #define P_PERF_FMT_INTERVAL "megabytes=%.4f real_sec=%.2f rate_Mbps=%.4f" #define P_PERF_FMT_INTERVAL2 " total_megabytes=%.4f total_real_sec=%.2f" \ " total_rate_Mbps=%.4f" #define P_PERF_FMT_INTERVAL3 " tx_megabytes=%.4f" #define P_PERF_FMT_INTERVAL4 " tx_total_megabytes=%.4f" #define P_PERF_FMT_IN "megabytes=%lf real_seconds=%lf rate_KBps=%lf " \ "rate_Mbps=%lf\n" #define P_CPU_STATS_FMT_IN "user=%*f system=%*f elapsed=%*d:%*d cpu=%d%%" #define P_CPU_STATS_FMT_IN2 "user=%*f system=%*f elapsed=%*d:%*d:%*d cpu=%d%%" #define P_LOSS_FMT " data_loss=%.5f" #define P_LOSS_FMT_BRIEF " data_loss=%.5f" #define P_LOSS_FMT_INTERVAL " data_loss=%.5f" #define P_DROP_FMT " drop=%lld pkt=%lld" #define P_DROP_FMT_BRIEF " drop=%lld pkt=%lld" #define P_DROP_FMT_INTERVAL " drop=%lld pkt=%lld" #define P_JITTER_FMT "msminjitter=%.4f msavgjitter=%.4f " \ "msmaxjitter=%.4f" #define P_JITTER_MIN_FMT_BRIEF " msminjitter=%.4f" #define P_JITTER_AVG_FMT_BRIEF " msavgjitter=%.4f" #define P_JITTER_MAX_FMT_BRIEF " msmaxjitter=%.4f" #define P_JITTER_MIN_FMT_INTERVAL " msminjitter=%.4f" #define P_JITTER_AVG_FMT_INTERVAL " msavgjitter=%.4f" #define P_JITTER_MAX_FMT_INTERVAL " msmaxjitter=%.4f" #define P_JITTER_FMT_IN "jitter=%lf msavgjitter=%lf msmaxjitter=%lf" #define P_OWD_FMT "msminOWD=%.4f msavgOWD=%.4f msmaxOWD=%.4f" #define P_OWD_MIN_FMT_BRIEF " msminOWD=%.4f" #define P_OWD_AVG_FMT_BRIEF " msavgOWD=%.4f" #define P_OWD_MAX_FMT_BRIEF " msmaxOWD=%.4f" #define P_OWD_MIN_FMT_INTERVAL " msminOWD=%.4f" #define P_OWD_AVG_FMT_INTERVAL " msavgOWD=%.4f" #define P_OWD_MAX_FMT_INTERVAL " msmaxOWD=%.4f" #define P_OWD_FMT_IN "OWD=%lf msavgOWD=%lf msmaxOWD=%lf" #define P_RETRANS_FMT "%sretrans=%d" #define P_RETRANS_FMT_STREAMS " retrans_by_stream=%d" #define P_RETRANS_FMT_BRIEF " %sretrans=%d" #define P_RETRANS_FMT_INTERVAL " %sretrans=%d" #define P_RETRANS_FMT_IN "retrans=%d" #define P_CWND_FMT " cwnd=%d" #define P_CWND_FMT_STREAMS " cwnd_by_stream=%d" #define P_CWND_FMT_BRIEF " cwnd=%d" #define P_CWND_FMT_INTERVAL " cwnd=%d" #define P_CWND_FMT_IN "cwnd=%d" #define P_RTT_FMT " rtt_ms=%.3f" #define P_RTT_FMT_BRIEF " rtt_ms=%.2f" #define P_RTT_FMT_IN "rtt_ms=%lf" #define HELO_FMT "HELO nuttcp v%d.%d.%d\n" #ifndef MAXSTREAM #define MAXSTREAM 128 #endif #define DEFAULT_NBUF 2048 #define DEFAULT_NBYTES 134217728 /* 128 MB */ #define DEFAULT_TIMEOUT 10.0 #define DEFAULT_UDP_RATE 1000 #define DEFAULTUDPBUFLEN 8192 #define DEFAULT_MC_UDPBUFLEN 1024 #define MAXUDPBUFLEN 65507 #define LOW_RATE_HOST3 1000 #define MINMALLOC 1024 #define HI_MC 231ul #define HI_MC_SSM 232ul /* locally defined global scope IPv6 multicast, FF3E::8000:0-FF3E::FFFF:FFFF */ #define HI_MC6 "FF3E::8000:0000" #define HI_MC6_LEN 13 #define HI_MC6_ASM "FF2E::0" #define HI_MC6_ASM_LEN 8 #ifndef LISTEN_BACKLOG #define LISTEN_BACKLOG 64 #endif #define ACCEPT_TIMEOUT 5 #ifndef MAX_CONNECT_TRIES #define MAX_CONNECT_TRIES 10 /* maximum server connect attempts */ #endif #ifndef SERVER_RETRY_USEC #define SERVER_RETRY_USEC 500000 /* server retry time in usec */ #endif #define MAX_EOT_WAIT_SEC 60.0 /* max wait for unsent data at EOT */ #define SRVR_INFO_TIMEOUT 60 /* timeout for reading server info */ #define IDLE_DATA_MIN 15.0 /* minimum value for chk_idle_data */ #define DEFAULT_IDLE_DATA 30.0 /* default value for chk_idle_data */ #define IDLE_DATA_MAX 60.0 /* maximum value for chk_idle_data */ #define NON_JUMBO_ETHER_MSS 1448 /* 1500 - 20:IP - 20:TCP -12:TCPOPTS */ #define TCP_UDP_HDRLEN_DELTA 12 /* difference in tcp & udp hdr sizes */ #define TCP_TIMESTAMPS_OPTLEN 12 /* size of TCP timestamps options */ #if defined(linux) #define TCP_ADV_WIN_SCALE "/proc/sys/net/ipv4/tcp_adv_win_scale" #endif #define DEBUGOUTPUT "/tmp/nuttcp-debugout.foo" #define BRIEF_RETRANS_STREAMS 0x2 /* brief per stream retrans info */ #define BRIEF_CWND_STREAMS 0x4 /* brief per stream cwnd info */ #define XMITSTATS 0x1 /* also give transmitter stats (MB) */ #define DEBUGINTERVAL 0x2 /* add info to assist with * debugging interval reports */ #define RUNNINGTOTAL 0x4 /* give cumulative stats for "-i" */ #define NODROPS 0x8 /* no packet drop stats for "-i" */ #define NOPERCENTLOSS 0x10 /* don't give percent loss for "-i" */ #define DEBUGPOLL 0x20 /* add info to assist with debugging * polling for interval reports */ #define PARSE 0x40 /* generate key=value parsable output */ #define DEBUGMTU 0x80 /* debug info for MTU/MSS code */ #define NORETRANS 0x100 /* no retrans stats for "-i" */ #define DEBUGRETRANS 0x200 /* output info for debugging collection * of TCP retransmission info */ #define NOBETAMSG 0x400 /* suppress beta version message */ #define WANTRTT 0x800 /* output RTT info (default) */ #define DEBUGJITTER 0x1000 /* debugging info for jitter option */ #define NOCWND 0x2000 /* no cwnd stats for "-i" */ #define DEBUGIRATE 0x4000 /* debugging info for irate option */ #ifdef NO_IPV6 /* Build without IPv6 support */ #undef AF_INET6 #undef IPV6_V6ONLY #endif void sigpipe( int signum ); void sigint( int signum ); void ignore_alarm( int signum ); void sigalarm( int signum ); static void err( char *s ); static void mes( char *s ); static void errmes( char *s ); void pattern( char *cp, int cnt ); void get_timeofday( struct timeval *tv, struct timezone *tz ); void prep_timer(); double read_timer( char *str, int len ); static void prusage( struct rusage *r0, struct rusage *r1, struct timeval *e, struct timeval *b, char *outp ); static void tvadd( struct timeval *tsum, struct timeval *t0, struct timeval *t1 ); static void tvsub( struct timeval *tdiff, struct timeval *t1, struct timeval *t0 ); static void psecs( long l, char *cp ); int Nread( int fd, char *buf, int count ); int Nwrite( int fd, char *buf, int count ); int delay( int us ); int mread( int fd, char *bufp, unsigned n ); int mwrite( int fd, char *bufp, unsigned n, int last_write ); char *getoptvalp( char **argv, int index, int reqval, int *skiparg ); #if defined(linux) && defined(TCPI_OPT_TIMESTAMPS) void print_tcpinfo(); #endif int vers_major = 8; int vers_minor = 1; int vers_delta = 2; int ivers; int rvers_major = 0; int rvers_minor = 0; int rvers_delta = 0; int irvers; int beta = 0; struct sockaddr_in sinme[MAXSTREAM + 1]; struct sockaddr_in sinhim[MAXSTREAM + 1]; struct sockaddr_in save_sinhim, save_mc; #ifdef AF_INET6 struct sockaddr_in6 sinme6[MAXSTREAM + 1]; struct sockaddr_in6 sinhim6[MAXSTREAM + 1]; struct sockaddr_in6 save_sinhim6, save_mc6; struct in6_addr hi_mc6, hi_mc6_asm; #endif struct sockaddr_storage frominet; int domain = PF_UNSPEC; int af = AF_UNSPEC; int mc_af = AF_UNSPEC; int explicitaf = 0; /* address family explicit specified (-4|-6) */ int fd[MAXSTREAM + 1]; /* fd array of network sockets */ int nfd; /* fd for accept call */ struct pollfd pollfds[MAXSTREAM + 4]; /* used for reading interval reports */ socklen_t fromlen; int buflen = 64 * 1024; /* length of buffer */ int nbuflen; int mallocsize; char *buf; /* ptr to dynamic buffer */ unsigned long long nbuf = 0; /* number of buffers to send in sinkmode */ int nbuf_bytes = 0; /* set to 1 if nbuf is actually bytes */ /* nick code */ int sendwin=0, sendwinval=0, origsendwin=0; socklen_t optlen; int rcvwin=0, rcvwinval=0, origrcvwin=0; int srvrwin=0; /* end nick code */ #if defined(linux) int sendwinavail=0, rcvwinavail=0, winadjust=0; #endif #if defined(linux) && defined(TCPI_OPT_TIMESTAMPS) #ifdef OLD_TCP_INFO #define STRUCT_TCPINFO tcpinfo struct tcpinfo { /* for collecting TCP retransmission info */ struct tcp_info _tcpinf; /* add missing structure elements */ u_int32_t tcpi_rcv_rtt; u_int32_t tcpi_rcv_space; u_int32_t tcpi_total_retrans; } tcpinf; #define tcpinfo_state _tcpinf.tcpi_state #define tcpinfo_ca_state _tcpinf.tcpi_ca_state #define tcpinfo_retransmits _tcpinf.tcpi_retransmits #define tcpinfo_unacked _tcpinf.tcpi_unacked #define tcpinfo_sacked _tcpinf.tcpi_sacked #define tcpinfo_lost _tcpinf.tcpi_lost #define tcpinfo_retrans _tcpinf.tcpi_retrans #define tcpinfo_fackets _tcpinf.tcpi_fackets #define tcpinfo_rtt _tcpinf.tcpi_rtt #define tcpinfo_rttvar _tcpinf.tcpi_rttvar #define tcpinfo_snd_ssthresh _tcpinf.tcpi_snd_ssthresh #define tcpinfo_snd_cwnd _tcpinf.tcpi_snd_cwnd #else #define STRUCT_TCPINFO tcp_info struct tcp_info tcpinf; #define tcpinfo_state tcpi_state #define tcpinfo_ca_state tcpi_ca_state #define tcpinfo_retransmits tcpi_retransmits #define tcpinfo_unacked tcpi_unacked #define tcpinfo_sacked tcpi_sacked #define tcpinfo_lost tcpi_lost #define tcpinfo_retrans tcpi_retrans #define tcpinfo_fackets tcpi_fackets #define tcpinfo_rtt tcpi_rtt #define tcpinfo_rttvar tcpi_rttvar #define tcpinfo_snd_ssthresh tcpi_snd_ssthresh #define tcpinfo_snd_cwnd tcpi_snd_cwnd #endif #else #define STRUCT_TCPINFO dummy_tcp_info struct dummy_tcp_info { int dummy1; } tcpinf; #endif int get_retrans( int sockfd, struct STRUCT_TCPINFO *tcpinfo ); int udp = 0; /* 0 = tcp, !0 = udp */ int udplossinfo = 0; /* set to 1 to give UDP loss info for * interval reporting */ int do_jitter = 0; /* set to 1 to enable jitter measurements */ int do_owd = 0; /* set to 1 to enable one-way delay reports */ int retransinfo = 0; /* set to 1 to give TCP retransmission info * for interval reporting */ int force_retrans = 0; /* set to force sending retrans info */ int send_retrans = 1; /* set to 0 if no need to send retrans info */ int do_retrans = 0; /* set to 1 for client transmitter */ int read_retrans = 1; /* set to 0 if no need to read retrans info */ int got_0retrans = 0; /* set to 1 by client transmitter after * processing initial server output * having "0 retrans" */ int init_pkt_cwnd = 0; /* initial congestion window in packets */ int sss_pkt_cwnd = 0; /* smoothed slow start congestion window */ int cwndinfo = 0; /* set to 1 to give TCP congestion window info * for interval reporting */ int send_cwnd = 1; /* set to 0 if no need to send cwnd info */ int do_cwnd = 0; /* set to 1 for client transmitter */ int read_cwnd = 1; /* set to 0 if no need to read cwnd info */ int need_swap; /* client and server are different endian */ int options = 0; /* socket options */ int one = 1; /* for 4.3 BSD style setsockopt() */ /* default port numbers if command arg or getserv doesn't get a port # */ #define DEFAULT_PORT 5101 #define DEFAULT_CTLPORT 5000 #define IPERF_PORT 5001 unsigned short port = 0; /* TCP port number */ unsigned short srcport = 0; /* TCP source port */ unsigned short ctlport = 0; /* control port for server connection */ unsigned short srcctlport = 0; /* TCP source port for server connection */ unsigned short ctlport3 = 0; /* control port for 3rd party server conn */ int tmpport; char *host; /* ptr to name of host */ char *stride = NULL; /* ptr to address stride for multi-stream */ char *host3 = NULL; /* ptr to 3rd party host */ int thirdparty = 0; /* set to 1 indicates doing 3rd party nuttcp */ int no3rd = 0; /* set to 1 by server to disallow 3rd party */ int forked = 0; /* set to 1 after server has forked */ int pass_ctlport = 0; /* set to 1 to use same outgoing control port as incoming with 3rd party usage */ char *nut_cmd; /* command used to invoke nuttcp */ char *cmdargs[50]; /* command arguments array */ char tmpargs[50][50]; #ifndef AF_INET6 #define ADDRSTRLEN 16 #else #define ADDRSTRLEN INET6_ADDRSTRLEN int v4mapped = 0; /* set to 1 to enable v4 mapping in v6 server */ #endif #define HOSTNAMELEN 80 #define HOST3BUFLEN HOSTNAMELEN + 2 + ADDRSTRLEN + 1 + ADDRSTRLEN /* host3=[=]host3addr[+host3stride] */ char hostbuf[ADDRSTRLEN]; /* buffer to hold text of address */ char host3addr[ADDRSTRLEN]; /* buffer to hold text of 3rd party address */ char host3buf[HOST3BUFLEN + 1]; /* buffer to hold 3rd party name or address */ char clientbuf[NI_MAXHOST]; /* buffer to hold client's resolved hostname */ int trans = 1; /* 0=receive, !0=transmit mode */ int sinkmode = 1; /* 0=normal I/O, !0=sink/source mode */ #if defined(linux) int zerocopy = 0; /* set to enable zero copy via sendfile() */ int directio = 0; /* set to enable direct I/O */ #endif int nofork = 0; /* set to 1 to not fork server */ int verbose = 0; /* 0=print basic info, 1=print cpu rate, proc * resource usage. */ int nodelay = 0; /* set TCP_NODELAY socket option */ unsigned long rate = MAXRATE; /* transmit rate limit in Kbps */ int maxburst = 1; /* number of packets allowed to exceed rate */ int nburst = 1; /* number of packets currently exceeding rate */ int irate = -1; /* instantaneous rate limit if set */ int iratesss = 0; /* set if emulating smoothed slow start */ double pkt_time; /* packet transmission time in seconds */ double pkt_time_ms; /* packet transmission time in milliseconds */ uint64_t irate_pk_usec; /* packet transmission time in microseconds */ double irate_pk_nsec; /* nanosecond portion of pkt xmit time */ double irate_cum_nsec = 0.0; /* cumulative nanaseconds over several pkts */ int rate_pps = 0; /* set to 1 if rate is given as pps */ double timeout = 0.0; /* timeout interval in seconds */ double interval = 0.0; /* interval timer in seconds */ double chk_idle_data = 0.0; /* server receiver checks this often */ /* for client having gone away */ double chk_interval = 0.0; /* timer (in seconds) for checking client */ int ctlconnmss; /* control connection maximum segment size */ int datamss = 0; /* data connection maximum segment size */ unsigned int tos = 0; /* 8-bit TOS field for setting DSCP/TOS */ char intervalbuf[256+2]; /* buf for interval reporting */ char linebuf[256+2]; /* line buffer */ int do_poll = 0; /* set to read interval reports (client xmit) */ int got_done = 0; /* set when read last of interval reports */ int reverse = 0; /* reverse direction of data connection open */ int format = 0; /* controls formatting of output */ char fmt[257]; int traceroute = 0; /* do traceroute back to client if set */ int skip_data = 0; /* skip opening of data channel */ #if defined(linux) int multicast = 0; /* set to 1 for multicast UDP transfer */ #else uint8_t multicast = 0; /* set to 1 for multicast UDP transfer */ #endif int ssm = -1; /* set to 1 for Source Specific Multicast */ /* set to 0 to NOT do SSM */ /* set to -1 to have SSM follow protocol */ /* (off for ipv4, on for ipv6) */ int mc_param; char *mc_addr = NULL; /* user specified multicast IP address */ char mcgaddr[ADDRSTRLEN]; /* buffer to hold text of MC group address */ struct ip_mreq mc_group; /* holds multicast group address */ #ifdef AF_INET6 struct ipv6_mreq mc6_group; /* holds multicast group address */ #endif #ifdef MCAST_JOIN_SOURCE_GROUP struct group_source_req group_source_req; /* holds multicast SSM group and */ /* source information */ #endif #ifdef HAVE_SETPRIO int priority = 0; /* nuttcp process priority */ #endif /* affinity and srvr_affinity need to be defined even if don't * HAVE_SETAFFINITY, to make parameter passing between client and * server work out OK, since far end may HAVE_SETAFFINITY * * they are set to -1 so they have no effect even if don't * HAVE_SETAFFINITY */ int affinity = -1; /* nuttcp process CPU affinity */ int srvr_affinity = -1; /* nuttcp server process CPU affinity */ #ifdef HAVE_SETAFFINITY int ncores = 1; /* number of CPU cores */ cpu_set_t cpu_set; /* processor CPU set */ #endif long timeout_sec = 0; struct itimerval itimer; /* for setitimer */ int srvr_helo = 1; /* set to 0 if server doesn't send HELO */ char ident[40 + 1 + 1] = ""; /* identifier for nuttcp output */ int intr = 0; int abortconn = 0; int braindead = 0; /* for braindead Solaris 2.8 systems */ int brief = 1; /* set for brief output */ int brief3 = 1; /* for third party nuttcp */ int done = 0; /* don't output interval report if done */ int got_begin = 0; /* don't output interval report if not begun */ int two_bod = 0; /* newer versions send 2 BOD packets for UDP */ int handle_urg = 0; /* newer versions send/recv urgent TCP data */ int got_eod0 = 0; /* got EOD0 packet - marks end of UDP xfer */ int buflenopt = 0; /* whether or not user specified buflen */ int haverateopt = 0; /* whether or not user specified rate */ int clientserver = 0; /* client server mode (use control channel) */ int client = 0; /* 0=server side, 1=client (initiator) side */ int oneshot = 0; /* 1=run server only once */ int inetd = 0; /* set to 1 if server run from inetd */ pid_t pid; /* process id when forking server process */ pid_t wait_pid; /* return of wait system call */ int pidstat; /* status of forked process */ FILE *ctlconn; /* uses fd[0] for control channel */ FILE *debugout; /* used for voluminous nuttcp debug output */ int savestdin; /* used to save real standard in */ int savestdout; /* used to save real standard out */ int firsttime = 1; /* flag for first pass through server */ struct in_addr clientaddr; /* IP address of client connecting to server */ #ifdef AF_INET6 struct in6_addr clientaddr6; /* IP address of client connecting to server */ uint32_t clientscope6; /* scope part of IP address of client */ #endif struct hostent *addr; extern int errno; const char Usage[] = "\ Usage: nuttcp or nuttcp -h prints this usage info\n\ Usage: nuttcp -V prints version info\n\ Usage: nuttcp -xt [-m] host forward and reverse traceroute to/from server\n\ Usage (transmitter): nuttcp [-t] [-options] [ctl_addr/]host [3rd-party] [out]\n\ -4 Use IPv4\n" #ifdef AF_INET6 " -6 Use IPv6\n" #endif " -c## cos dscp value on data streams (t|T suffix for full TOS field)\n\ -l## length of network write|read buf (default 1K|8K/udp, 64K/tcp)\n" #if defined(linux) " -s[d][z] use stdin|stdout for data input|output instead of pattern data\n" " ('d' suboption uses direct I/O if input|output is regular file)\n" " ('z' suboption enables zero copy tx if input is regular file)\n" #else " -s use stdin|stdout for data input|output instead of pattern data\n" #endif " -n## number of source bufs written to network (default unlimited)\n\ -w## transmitter|receiver window size in KB (or (m|M)B or (g|G)B)\n\ -ws## server receive|transmit window size in KB (or (m|M)B or (g|G)B)\n\ -wb braindead Solaris 2.8 (sets both xmit and rcv windows)\n\ -p## port number to send to|listen at (default 5101)\n\ -p#:# specify both source:destination port for -p option\n\ -P## port number for control connection (default 5000)\n\ -P#:# specify both source:destination port for -P option\n\ -P#/# control port to/from 3rd-party host (default 5000)\n\ -u use UDP instead of TCP\n\ -m## use multicast with specified TTL instead of unicast (UDP)\n\ -gxxx user specified multicast IP address for -m option\n\ -M## MSS for data connection (TCP)\n\ -N## number of streams (starting at port number), implies -B\n\ -R## transmit rate limit in Kbps (or (m|M)bps or (g|G)bps or (p)ps)\n\ -Ri#[/#] instantaneous rate limit with optional packet burst\n" #if defined(linux) " -Ris## emulated smoothed slow start option for -Ri option (TCP)\n" #endif " -T## transmit timeout in seconds (or (m|M)inutes or (h|H)ours)\n\ -j enable jitter measurements (assumes -u and -Ri options)\n\ -o enable one-way delay reports (needs synchronized clocks)\n\ -i## receiver interval reporting in seconds (or (m|M)inutes)\n\ -Ixxx identifier for nuttcp output (max of 40 characters)\n\ -F flip option to reverse direction of data connection open\n\ -a retry failed server connection \"again\" for transient errors\n" #ifdef HAVE_SETPRIO " -xP## set nuttcp process priority (must be root)\n" #endif #ifdef HAVE_SETAFFINITY " -xc## set nuttcp client process CPU affinity\n" " -xcs## set nuttcp server process CPU affinity\n" " -xc#/# set nuttcp client/server process CPU affinity\n" #endif " -d set TCP SO_DEBUG option on data socket\n\ -v[v] verbose [or very verbose] output\n\ -b brief output (default)\n\ -br add per-stream TCP retrans info to brief summary (Linux only)\n" #if defined(linux) " -bc add per-stream TCP cwnd info to brief summary (Linux only)\n" #endif " -D xmit only: don't buffer TCP writes (sets TCP_NODELAY sockopt)\n\ -B recv only: only output full blocks of size from -l## (for TAR)\n" " --packet-burst packet burst value for instantaneous rate limit option\n" " --idle-data-timeout (default: 15/30/60)\n" " client timeout in seconds for idle data connection\n" #ifdef IPV6_V6ONLY " --disable-v4-mapped disable v4 mapping in v6 server (default)\n" " --enable-v4-mapped enable v4 mapping in v6 server\n" #endif "\n\ Usage (server): nuttcp -S[P] [-options]\n\ note server mode excludes use of -s except for -1 one-shot mode\n\ 'P' suboption makes 3rd party {in,out}bound control ports same\n\ -4 Use IPv4 (default)\n" #ifdef AF_INET6 " -6 Use IPv6\n" #endif " -1 oneshot server mode (implied with inetd/xinetd), implies -S\n" #if defined(linux) " -s[d][z] use stdin|stdout for data input|output instead of pattern data\n" " ('d' suboption uses direct I/O if input|output is regular file)\n" " ('z' suboption enables zero copy tx if input is regular file)\n" #else " -s use stdin|stdout for data input|output instead of pattern data\n" #endif " -P## port number for server connection (default 5000)\n\ note don't use with inetd/xinetd (use services file instead)\n" #ifdef HAVE_SETPRIO " -xP## set nuttcp process priority (must be root)\n" #endif #ifdef HAVE_SETAFFINITY " -xc## set nuttcp server process CPU affinity\n" #endif " --idle-data-timeout (default: 15/30/60)\n" " server timeout in seconds for idle data connection\n" " --no3rdparty don't allow 3rd party capability\n" " --nofork don't fork server\n" " --single-threaded make manually started server be single threaded\n" #ifdef IPV6_V6ONLY " --disable-v4-mapped disable v4 mapping in v6 server (default)\n" " --enable-v4-mapped enable v4 mapping in v6 server\n" #endif "\n\ Multilink aggregation options (TCP only):\n\ nuttcp [-options] -N## [ctl_addr]/host1/host2/.../host## (xmit only)\n\ nuttcp [-options] -N## [ctl_addr/]host+addr_stride (IPv4 only)\n\ nuttcp [-options] -N## [ctl_addr/]host+n.n.n.n (IPv4 only)\n\ nuttcp [-options] -N##m [ctl_addr/]host\n\ where host resolves to multiple addresses\n\ \n\ separate [ctl_addr/] option available only for xmit\n\ \n\ Format options:\n\ -fxmitstats also give transmitter stats (MB) with -i (UDP only)\n\ -frunningtotal also give cumulative stats on interval reports\n\ -f-drops don't give packet drop info on brief output (UDP)\n\ -f-retrans don't give retrans info on brief output (TCP)\n" #if defined(linux) " -f-cwnd don't give cwnd info on brief output (TCP)\n" #endif " -f-percentloss don't give %%loss info on brief output (UDP)\n\ -fparse generate key=value parsable output\n\ -f-beta suppress beta version message\n\ -f-rtt suppress RTT info \n\ "; char stats[128]; char srvrbuf[4096]; char tmpbuf[257]; uint64_t nbytes = 0; /* bytes on net */ int64_t pbytes = 0; /* previous bytes - for interval reporting */ int64_t ntbytes = 0; /* bytes sent by transmitter */ int64_t ptbytes = 0; /* previous bytes sent by transmitter */ uint64_t ntbytesc = 0; /* bytes sent by transmitter that have * been counted */ uint64_t ntbytescp = 0; /* previous ntbytesc count */ uint64_t ntbytescpi = 0; /* ntbytescp for interval reports */ uint64_t chk_nbytes = 0; /* byte counter used to test if no more data * being received by server (presumably because * client transmitter went away */ double rtt = 0.0; /* RTT between client and server in ms */ int which_rt = 1; /* which round trip for "-Ris" */ uint32_t nretrans[MAXSTREAM+1]; /* number of TCP retransmissions */ uint32_t iretrans[MAXSTREAM+1]; /* initial number of TCP retransmissions */ uint32_t pretrans = 0; /* previous number of TCP retransmissions */ uint32_t sretrans = 0; /* number of system TCP retransmissions */ uint32_t cwnd[MAXSTREAM+1]; /* TCP congestion window in KB */ int numCalls = 0; /* # of NRead/NWrite calls. */ int nstream = 1; /* number of streams */ int multilink = 0; /* set to use multilink aggregation */ int stream_idx = 0; /* current stream */ int start_idx = 1; /* set to use or bypass control channel */ int b_flag = 1; /* use mread() */ int got_srvr_output = 0; /* set when server output has been read */ int reading_srvr_info = 0; /* set when starting to read server info */ int retry_server = 0; /* set to retry control connect() to server */ int num_connect_tries = 0; /* tracks attempted connects to server */ int single_threaded = 0; /* set to make server single threaded */ double srvr_MB; double srvr_realt; double srvr_KBps; double srvr_Mbps; int srvr_cpu_util; double cput = 0.000001, realt = 0.000001; /* user, real time (seconds) */ double realtd = 0.000001; /* real time delta - for interval reporting */ double pkt_delta; /* time delta between packets in ms */ double jitter; /* current jitter measurement in ms */ unsigned long long njitter; /* number of jitter measurements */ double jitter_min; /* jitter minimum */ double jitter_max; /* jitter maximum */ double jitter_avg; /* jitter average */ double jitteri; /* current jitter interval measurement in ms */ unsigned long long njitteri; /* number of jitter interval measurements */ double jitter_mini; /* jitter minimum for interval report */ double jitter_maxi; /* jitter maximum for interval report */ double jitter_avgi; /* jitter average for interval report */ double owd; /* current one-way delay measurement in ms */ unsigned long long nowd; /* number of one-way delay measurements */ double owd_min; /* one-way delay minimum */ double owd_max; /* one-way delay maximum */ double owd_avg; /* one-way delay average */ unsigned long long nowdi; /* number of OWD interval measurements */ double owd_mini; /* OWD minimum for interval report */ double owd_maxi; /* OWD maximum for interval report */ double owd_avgi; /* OWD average for interval report */ void close_data_channels() { if (fd[1] == -1) return; if (clientserver && client && !host3 && udp && trans) { /* If all the EOD packets get lost at the end of a UDP * transfer, having the client do a shutdown() for writing * on the control connection allows the server to more * quickly realize that the UDP transfer has completed * (mostly of benefit for separate control and data paths) * * Can't do this in the opposite direction since the * server needs to send info back to client */ shutdown(0, SHUT_WR); } if (multicast && !trans) { /* Leave the multicast group */ if ((af == AF_INET) && !ssm) { if (setsockopt(fd[1], IPPROTO_IP, IP_DROP_MEMBERSHIP, (void *)&mc_group, sizeof(mc_group)) < 0) { err("setsockopt: IP_DROP_MEMBERSHIP"); } } #ifdef AF_INET6 else if ((af == AF_INET6) && !ssm) { if (setsockopt(fd[1], IPPROTO_IPV6, IPV6_LEAVE_GROUP, (void *)&mc6_group, sizeof(mc6_group)) < 0) { err("setsockopt: IPV6_LEAVE_GROUP"); } } #endif #ifdef MCAST_JOIN_SOURCE_GROUP else if ((af == AF_INET) && ssm) { /* Leave the source specific multicast group */ if (setsockopt(fd[1], IPPROTO_IP, MCAST_LEAVE_SOURCE_GROUP, &group_source_req, sizeof(group_source_req)) < 0) { err("setsockopt: MCAST_LEAVE_SOURCE_GROUP"); } } #ifdef AF_INET6 else if ((af == AF_INET6) && ssm) { /* Leave the source specific multicast group */ if (setsockopt(fd[1], IPPROTO_IPV6, MCAST_LEAVE_SOURCE_GROUP, &group_source_req, sizeof(group_source_req)) < 0) { err("setsockopt: MCAST_LEAVE_SOURCE_GROUP"); } } #endif /* AF_INET6 */ #endif /* MCAST_JOIN_SOURCE_GROUP */ } for ( stream_idx = 1; stream_idx <= nstream; stream_idx++ ) { close(fd[stream_idx]); fd[stream_idx] = -1; } } #ifdef SIGPIPE void sigpipe( int signum ) { signal(SIGPIPE, sigpipe); } #endif void sigint( int signum ) { signal(SIGINT, SIG_DFL); fputs("\n*** transfer interrupted ***\n", stdout); if (clientserver && client && !host3 && udp && !trans) shutdown(0, SHUT_WR); else intr = 1; done++; return; } void ignore_alarm( int signum ) { return; } void sigalarm( int signum ) { struct timeval timec; /* Current time */ struct timeval timed; /* Delta time */ int64_t nrbytes; uint64_t deltarbytes, deltatbytes; double fractloss; int nodata; int i; char *cp1, *cp2; short save_events; long flags, saveflags; if (host3 && clientserver) { if (client) intr = 1; return; } if (clientserver && client && reading_srvr_info) { mes("Error: not receiving server info"); exit(1); } #ifdef HAVE_CLOCK_GETTIME timec.tv_sec = 0; /* silence bogus compiler warning */ timec.tv_usec = 0; /* silence bogus compiler warning */ #endif if (interval && !trans) { /* Get real time */ get_timeofday(&timec, (struct timezone *)0); tvsub( &timed, &timec, &timep ); realtd = timed.tv_sec + ((double)timed.tv_usec) / 1000000; if (realtd <= 0.0) realtd = 0.000001; tvsub( &timed, &timec, &time0 ); realt = timed.tv_sec + ((double)timed.tv_usec) / 1000000; if (realt <= 0.0) realt = 0.000001; } if (clientserver && !trans) { struct sockaddr_in peer; socklen_t peerlen = sizeof(peer); nodata = 0; if (getpeername(fd[0], (struct sockaddr *)&peer, &peerlen) < 0) nodata = 1; if (!client && udp && got_begin) { /* checks if client did a shutdown() for writing * on the control connection */ pollfds[0].fd = fileno(ctlconn); save_events = pollfds[0].events; pollfds[0].events = POLLIN | POLLPRI; pollfds[0].revents = 0; if ((poll(pollfds, 1, 0) > 0) && (pollfds[0].revents & (POLLIN | POLLPRI))) { nodata = 1; } pollfds[0].events = save_events; } if (interval) { chk_interval += realtd; if (chk_interval >= chk_idle_data) { chk_interval = 0; if ((nbytes - chk_nbytes) == 0) nodata = 1; chk_nbytes = nbytes; } } else { if ((nbytes - chk_nbytes) == 0) nodata = 1; chk_nbytes = nbytes; } if (nodata) { /* Don't just exit anymore so can get partial results * (shouldn't be a problem but keep an eye out that * servers don't start hanging again) */ if (!client && udp && !interval && handle_urg) { /* send 'A' for ABORT as urgent TCP data * on control connection (don't block) * * Only server can do this since client * does a shutdown() for writing on the * control connection */ saveflags = fcntl(fd[0], F_GETFL, 0); if (saveflags != -1) { flags = saveflags | O_NONBLOCK; fcntl(fd[0], F_SETFL, flags); } send(fd[0], "A", 1, MSG_OOB); if (saveflags != -1) { flags = saveflags; fcntl(fd[0], F_SETFL, flags); } } if (client) { mes("Error: not receiving data from server"); exit(1); } close_data_channels(); intr = 1; return; } if (!interval) return; } if (interval && !trans) { if ((udp && !got_begin) || done) { timep.tv_sec = timec.tv_sec; timep.tv_usec = timec.tv_usec; return; } if (clientserver) { nrbytes = nbytes; if (udplossinfo) { ntbytes = *(int64_t *)(buf + 24); if (need_swap) { cp1 = (char *)&ntbytes; cp2 = buf + 31; for ( i = 0; i < 8; i++ ) *cp1++ = *cp2--; } if (ntbytes > ntbytesc) /* received bytes not counted yet */ nrbytes += buflen; if ((nrbytes > ntbytes) || ((nrbytes - pbytes) > (ntbytes - ptbytes))) /* yes they were counted */ nrbytes -= buflen; } if (read_retrans) { nretrans[1] = *(uint32_t *)(buf + 24); if (need_swap) { cp1 = (char *)&nretrans[1]; cp2 = buf + 27; for ( i = 0; i < 4; i++ ) *cp1++ = *cp2--; } } if (read_cwnd) { cwnd[1] = *(uint32_t *)(buf + 28); if (need_swap) { cp1 = (char *)&cwnd[1]; cp2 = buf + 31; for ( i = 0; i < 4; i++ ) *cp1++ = *cp2--; } if ((cwnd[1] == 0x5254524Eu) || /* "RTRN" */ (cwnd[1] == 0x48525452u)) /* "HRTR" */ cwnd[1] = init_pkt_cwnd; } if (*ident) fprintf(stdout, "%s: ", ident + 1); if (format & PARSE) strcpy(fmt, P_PERF_FMT_INTERVAL); else strcpy(fmt, PERF_FMT_INTERVAL); fprintf(stdout, fmt, (double)(nrbytes - pbytes)/(1024*1024), realtd, (double)(nrbytes - pbytes)/realtd/125000); if (udplossinfo) { if (!(format & NODROPS)) { if (format & PARSE) strcpy(fmt, P_DROP_FMT_INTERVAL); else strcpy(fmt, DROP_FMT_INTERVAL); fprintf(stdout, fmt, ((ntbytes - ptbytes) - (nrbytes - pbytes)) /buflen, (ntbytes - ptbytes)/buflen); } if (!(format & NOPERCENTLOSS)) { deltarbytes = nrbytes - pbytes; deltatbytes = ntbytes - ptbytes; fractloss = (deltatbytes ? 1.0 - (double)deltarbytes /(double)deltatbytes : 0.0); if (format & PARSE) strcpy(fmt, P_LOSS_FMT_INTERVAL); else if ((fractloss != 0.0) && (fractloss < 0.001)) strcpy(fmt, LOSS_FMT_INTERVAL5); else strcpy(fmt, LOSS_FMT_INTERVAL); fprintf(stdout, fmt, fractloss * 100); } } if ((do_jitter & JITTER_MIN) && njitteri) { if (format & PARSE) strcpy(fmt, P_JITTER_MIN_FMT_INTERVAL); else strcpy(fmt, JITTER_MIN_FMT_INTERVAL); fprintf(stdout, fmt, jitter_mini); } if ((do_jitter & JITTER_AVG) && njitteri) { if (format & PARSE) strcpy(fmt, P_JITTER_AVG_FMT_INTERVAL); else strcpy(fmt, JITTER_AVG_FMT_INTERVAL); fprintf(stdout, fmt, jitter_avgi/njitteri); } if ((do_jitter & JITTER_MAX) && njitteri) { if (format & PARSE) strcpy(fmt, P_JITTER_MAX_FMT_INTERVAL); else strcpy(fmt, JITTER_MAX_FMT_INTERVAL); fprintf(stdout, fmt, jitter_maxi); } if (do_jitter && njitteri) { njitteri = 0; jitter_mini = 1000000.0; jitter_maxi = -1000000.0; jitter_avgi = 0.0; } if (read_retrans && sinkmode) { if (format & PARSE) fprintf(stdout, P_RETRANS_FMT_INTERVAL, ((retransinfo == 1) || !nrbytes) ? "" : "host-", (nretrans[1] - pretrans)); else fprintf(stdout, RETRANS_FMT_INTERVAL, (nretrans[1] - pretrans), ((retransinfo == 1) || !nrbytes) ? "" : "host-"); } if (read_cwnd && sinkmode) { if (format & PARSE) fprintf(stdout, P_CWND_FMT_INTERVAL, cwnd[1]); else fprintf(stdout, CWND_FMT_INTERVAL, cwnd[1]); } if ((do_owd & OWD_MIN) && nowdi) { if (format & PARSE) strcpy(fmt, P_OWD_MIN_FMT_INTERVAL); else strcpy(fmt, OWD_MIN_FMT_INTERVAL); fprintf(stdout, fmt, owd_mini); } if ((do_owd & OWD_AVG) && nowdi) { if (format & PARSE) strcpy(fmt, P_OWD_AVG_FMT_INTERVAL); else strcpy(fmt, OWD_AVG_FMT_INTERVAL); fprintf(stdout, fmt, owd_avgi/nowdi); } if ((do_owd & OWD_MAX) && nowdi) { if (format & PARSE) strcpy(fmt, P_OWD_MAX_FMT_INTERVAL); else strcpy(fmt, OWD_MAX_FMT_INTERVAL); fprintf(stdout, fmt, owd_maxi); } if (do_owd && nowdi) { nowdi = 0; owd_mini = 1000000.0; owd_maxi = -1000000.0; owd_avgi = 0.0; } if (format & RUNNINGTOTAL) { if (format & PARSE) strcpy(fmt, P_PERF_FMT_INTERVAL2); else strcpy(fmt, PERF_FMT_INTERVAL2); fprintf(stdout, fmt, (double)nrbytes/(1024*1024), realt, (double)nrbytes/realt/125000); if (udplossinfo) { if (!(format & NODROPS)) { if (format & PARSE) strcpy(fmt, P_DROP_FMT_INTERVAL); else strcpy(fmt, DROP_FMT_INTERVAL); fprintf(stdout, fmt, (ntbytes - nrbytes) /buflen, ntbytes/buflen); } if (!(format & NOPERCENTLOSS)) { fractloss = (ntbytes ? 1.0 - (double)nrbytes /(double)ntbytes : 0.0); if (format & PARSE) strcpy(fmt, P_LOSS_FMT_INTERVAL); else if ((fractloss != 0.0) && (fractloss < 0.001)) strcpy(fmt, LOSS_FMT_INTERVAL5); else strcpy(fmt, LOSS_FMT_INTERVAL); fprintf(stdout, fmt, fractloss * 100); } } if (read_retrans && sinkmode) { if (format & PARSE) fprintf(stdout, P_RETRANS_FMT_INTERVAL, ((retransinfo == 1) || !nrbytes) ? "" : "host-", nretrans[1]); else fprintf(stdout, RETRANS_FMT_INTERVAL, nretrans[1], ((retransinfo == 1) || !nrbytes) ? "" : "host-"); } if (read_cwnd && sinkmode) { if (format & PARSE) fprintf(stdout, P_CWND_FMT_INTERVAL, cwnd[1]); else fprintf(stdout, CWND_FMT_INTERVAL, cwnd[1]); } } if (udplossinfo && (format & XMITSTATS)) { if (format & PARSE) strcpy(fmt, P_PERF_FMT_INTERVAL3); else strcpy(fmt, PERF_FMT_INTERVAL3); fprintf(stdout, fmt, (double)(ntbytes - ptbytes)/1024/1024); if (format & RUNNINGTOTAL) { if (format & PARSE) strcpy(fmt, P_PERF_FMT_INTERVAL4); else strcpy(fmt, PERF_FMT_INTERVAL4); fprintf(stdout, fmt, (double)ntbytes/1024/1024); if (format & DEBUGINTERVAL) fprintf(stdout, " Pre: %.4f MB", (double)ntbytesc /1024/1024); } } fprintf(stdout, "\n"); fflush(stdout); timep.tv_sec = timec.tv_sec; timep.tv_usec = timec.tv_usec; pbytes = nrbytes; ptbytes = ntbytes; pretrans = nretrans[1]; } } else intr = 1; return; } int main( int argc, char **argv ) { double MB; double rate_opt; double fractloss; int cpu_util; int first_read; int first_jitter, first_jitteri; int ocorrection = 0; double correction = 0.0; int pollst = 0; int i = 0, j = 0; char *cp1 = NULL, *cp2 = NULL, *cp3 = NULL; char *hostaddr; char ch = '\0'; int error_num = 0; int sockopterr = 0; int save_errno; struct servent *sp = 0; struct addrinfo hints, *res[MAXSTREAM + 1] = { NULL }, *host3res, *mcres = NULL; union sockaddr_union client_ipaddr; struct sockaddr_storage dummy; struct timeval time_eod = {0}; /* time EOD packet was received */ struct timeval time_eod0 = {0}; /* time EOD0 packet was received */ struct timeval timed; /* time delta */ struct timeval timeconn1 = {0}; /* time before connect() for RTT */ struct timeval timeconn2 = {0}; /* time after connect() for RTT */ struct timeval timeconn; /* time to connect() == RTT */ union { unsigned char buf[sizeof(struct in_addr)]; uint32_t ip32; } ipad_stride; /* IPv4 address stride */ short save_events; int skiparg; int reqval; int got_srvr_retrans; int got_srvr_cwnd; uint32_t total_retrans = 0; /* total retrans for all streams */ uint32_t total_snd_cwnd = 0; /* total cwnd for all streams in KB */ double idle_data_min = IDLE_DATA_MIN; double idle_data_max = IDLE_DATA_MAX; double default_idle_data = DEFAULT_IDLE_DATA; char multsrc[ADDRSTRLEN] = "\0"; char multaddr[ADDRSTRLEN] = "\0"; long flags; int nameinfo_flags; int implicit_hostaddr; sendwin = 0; rcvwin = 0; srvrwin = -1; format |= WANTRTT; if (argc < 2) goto usage; nut_cmd = argv[0]; argv++; argc--; while (argc>0 && argv[0][0] == '-') { skiparg = 0; switch (argv[0][1]) { case '4': domain = PF_INET; af = AF_INET; explicitaf = 1; break; #ifdef AF_INET6 case '6': domain = PF_INET6; af = AF_INET6; explicitaf = 1; break; #endif case 'B': b_flag = 1; break; case 't': trans = 1; break; case 'r': trans = 0; break; case 'd': options |= SO_DEBUG; break; case 'D': nodelay = 1; break; case 'n': reqval = 0; if (argv[0][2] == 'b') { fprintf(stderr, "option \"-nb\" no longer supported, use \"-n###[k|m|g|t|p]\" instead\n"); fflush(stderr); exit(1); } cp1 = getoptvalp(argv, 2, reqval, &skiparg); nbuf = strtoull(cp1, NULL, 0); if (nbuf == 0) { if (errno == EINVAL) { fprintf(stderr, "invalid nbuf = %s\n", &argv[0][2]); fflush(stderr); exit(1); } else { nbuf = DEFAULT_NBUF; break; } } if (*cp1) ch = *(cp1 + strlen(cp1) - 1); else ch = '\0'; if ((ch == 'b') || (ch == 'B')) nbuf_bytes = 1; else if ((ch == 'k') || (ch == 'K')) { nbuf *= 1024; nbuf_bytes = 1; } else if ((ch == 'm') || (ch == 'M')) { nbuf *= 1048576; nbuf_bytes = 1; } else if ((ch == 'g') || (ch == 'G')) { nbuf *= 1073741824; nbuf_bytes = 1; } else if ((ch == 't') || (ch == 'T')) { nbuf *= 1099511627776ull; nbuf_bytes = 1; } else if ((ch == 'p') || (ch == 'P')) { nbuf *= 1125899906842624ull; nbuf_bytes = 1; } break; case 'l': reqval = 1; cp1 = getoptvalp(argv, 2, reqval, &skiparg); buflen = atoi(cp1); buflenopt = 1; if (buflen < 1) { fprintf(stderr, "invalid buflen = %d\n", buflen); fflush(stderr); exit(1); } if (*cp1) ch = *(cp1 + strlen(cp1) - 1); else ch = '\0'; if ((ch == 'k') || (ch == 'K')) buflen *= 1024; else if ((ch == 'm') || (ch == 'M')) buflen *= 1048576; break; case 'w': reqval = 1; if (argv[0][2] == 's') { cp1 = getoptvalp(argv, 3, reqval, &skiparg); srvrwin = atoi(cp1); if (*cp1) ch = *(cp1 + strlen(cp1) - 1); else ch = '\0'; if ((ch == 'k') || (ch == 'K')) srvrwin *= 1024; else if ((ch == 'm') || (ch == 'M')) srvrwin *= 1048576; else if ((ch == 'g') || (ch == 'G')) srvrwin *= 1073741824; else if ((ch != 'b') && (ch != 'B')) srvrwin *= 1024; if (srvrwin < 0) { fprintf(stderr, "invalid srvrwin = %d\n", srvrwin); fflush(stderr); exit(1); } } else { if (argv[0][2] == 'b') { braindead = 1; cp1 = getoptvalp(argv, 3, reqval, &skiparg); if (*cp1 == '\0') break; sendwin = atoi(cp1); } else { cp1 = getoptvalp(argv, 2, reqval, &skiparg); sendwin = atoi(cp1); } if (*cp1) ch = *(cp1 + strlen(cp1) - 1); else ch = '\0'; if ((ch == 'k') || (ch == 'K')) sendwin *= 1024; else if ((ch == 'm') || (ch == 'M')) sendwin *= 1048576; else if ((ch == 'g') || (ch == 'G')) sendwin *= 1073741824; else if ((ch != 'b') && (ch != 'B')) sendwin *= 1024; rcvwin = sendwin; if (sendwin < 0) { fprintf(stderr, "invalid sendwin = %d\n", sendwin); fflush(stderr); exit(1); } } if (srvrwin == -1) { srvrwin = sendwin; } break; case 's': sinkmode = 0; /* sink/source data */ #if defined(linux) if (strchr(argv[0], 'z')) zerocopy = 1; if (strchr(argv[0], 'd')) directio = 1; #endif break; case 'p': reqval = 1; cp1 = getoptvalp(argv, 2, reqval, &skiparg); if ((cp2 = strchr(cp1, ':'))) { tmpport = atoi(cp1); if ((tmpport < 1024) || (tmpport > 65535)) { fprintf(stderr, "invalid source port = %d\n", tmpport); fflush(stderr); exit(1); } srcport = tmpport; cp1 = cp2 + 1; } tmpport = atoi(cp1); if ((tmpport < 1024) || (tmpport > 65535)) { fprintf(stderr, "invalid port = %d\n", tmpport); fflush(stderr); exit(1); } port = tmpport; break; case 'P': reqval = 1; cp1 = getoptvalp(argv, 2, reqval, &skiparg); if ((cp2 = strchr(cp1, ':'))) { tmpport = atoi(cp1); if ((tmpport < 1024) || (tmpport > 65535)) { fprintf(stderr, "invalid source " "control port = %d\n", tmpport); fflush(stderr); exit(1); } srcctlport = tmpport; cp1 = cp2 + 1; } tmpport = atoi(cp1); if ((tmpport < 1024) || (tmpport > 65535)) { fprintf(stderr, "invalid ctlport = %d\n", tmpport); fflush(stderr); exit(1); } ctlport = tmpport; if ((cp2 = strchr(argv[0], '/'))) { if (strchr(cp2, ':')) { fprintf(stderr, "can't specify source control " "port with third party\n"); fflush(stderr); exit(1); } tmpport = atoi(cp2 + 1); if ((tmpport < 1024) || (tmpport > 65535)) { fprintf(stderr, "invalid third party " "ctlport = %d\n", tmpport); fflush(stderr); exit(1); } ctlport3 = tmpport; } break; case 'u': udp = 1; if (!buflenopt) buflen = DEFAULTUDPBUFLEN; if (argv[0][2] == 'u') { haverateopt = 1; rate = MAXRATE; } break; case 'j': reqval = 0; cp1 = getoptvalp(argv, 2, reqval, &skiparg); if (strchr(cp1, 'm')) do_jitter |= JITTER_MIN; if (strchr(cp1, 'a')) do_jitter |= JITTER_AVG; if (strchr(cp1, 'x')) do_jitter |= JITTER_MAX; if (do_jitter == 0) do_jitter = JITTER_MAX; if (strchr(cp1, 'o')) do_jitter |= JITTER_IGNORE_OOO; udp = 1; if (!buflenopt) buflen = DEFAULTUDPBUFLEN; break; case 'o': reqval = 0; cp1 = getoptvalp(argv, 2, reqval, &skiparg); if (strchr(cp1, 'm')) do_owd |= OWD_MIN; if (strchr(cp1, 'a')) do_owd |= OWD_AVG; if (strchr(cp1, 'x')) do_owd |= OWD_MAX; if (do_owd == 0) do_owd = OWD_AVG; break; case 'v': brief = 0; if (argv[0][2] == 'v') verbose = 1; break; case 'N': reqval = 1; cp1 = getoptvalp(argv, 2, reqval, &skiparg); nstream = atoi(cp1); if (strchr(cp1, 'm')) multilink = 1; if (nstream < 1) { fprintf(stderr, "invalid nstream = %d\n", nstream); fflush(stderr); exit(1); } if (nstream > MAXSTREAM) { fprintf(stderr, "nstream = %d > MAXSTREAM, set to %d\n", nstream, MAXSTREAM); nstream = MAXSTREAM; } if (nstream > 1) { b_flag = 1; send_retrans = 0; read_retrans = 0; send_cwnd = 0; read_cwnd = 0; } break; case 'R': reqval = 1; haverateopt = 1; if (argv[0][2] == 'i') { if (argv[0][3] == 's') { #if defined(linux) iratesss = 1; #else fprintf(stderr, "smoothed slow start not supported for non-Linux\n"); fflush(stderr); #endif cp1 = getoptvalp(argv, 4, reqval, &skiparg); } else { cp1 = getoptvalp(argv, 3, reqval, &skiparg); } sscanf(cp1, "%lf", &rate_opt); irate = 1; } else if (argv[0][2] == 'a') { cp1 = getoptvalp(argv, 3, reqval, &skiparg); sscanf(cp1, "%lf", &rate_opt); irate = 0; } else if (argv[0][2] == 'u') { rate_opt = 0.0; irate = 0; cp1 = &argv[0][3]; } else { cp1 = getoptvalp(argv, 2, reqval, &skiparg); sscanf(cp1, "%lf", &rate_opt); } if ((cp2 = strchr(cp1, '/'))) { *cp2++ = '\0'; maxburst = atoi(cp2); if (maxburst <= 0) { fprintf(stderr, "invalid maxburst = %d\n", maxburst); fflush(stderr); exit(1); } } if (*cp1) ch = *(cp1 + strlen(cp1) - 1); else ch = '\0'; if ((ch == 'm') || (ch == 'M')) rate_opt *= 1000; else if ((ch == 'g') || (ch == 'G')) rate_opt *= 1000000; else if (ch == 'p') { rate_pps = 1; if (strlen(cp1) >= 2) { ch = *(cp1 + strlen(cp1) - 2); if ((ch == 'k') || (ch == 'K')) rate_opt *= 1000; if ((ch == 'm') || (ch == 'M')) rate_opt *= 1000000; } } rate = rate_opt; if (rate == 0) rate = MAXRATE; break; case 'T': reqval = 0; cp1 = getoptvalp(argv, 2, reqval, &skiparg); sscanf(cp1, "%lf", &timeout); if (timeout < 0) { fprintf(stderr, "invalid timeout = %f\n", timeout); fflush(stderr); exit(1); } else if (timeout == 0.0) timeout = DEFAULT_TIMEOUT; if (*cp1) ch = *(cp1 + strlen(cp1) - 1); else ch = '\0'; if ((ch == 'm') || (ch == 'M')) timeout *= 60.0; else if ((ch == 'h') || (ch == 'H')) timeout *= 3600.0; else if ((ch == 'd') || (ch == 'D')) timeout *= 86400.0; itimer.it_value.tv_sec = timeout; itimer.it_value.tv_usec = (timeout - itimer.it_value.tv_sec)*1000000; if (timeout && !nbuf) nbuf = INT_MAX; break; case 'i': reqval = 0; cp1 = getoptvalp(argv, 2, reqval, &skiparg); sscanf(cp1, "%lf", &interval); if (interval < 0.0) { fprintf(stderr, "invalid interval = %f\n", interval); fflush(stderr); exit(1); } else if (interval == 0.0) interval = 1.0; if (*cp1) ch = *(cp1 + strlen(cp1) - 1); else ch = '\0'; if ((ch == 'm') || (ch == 'M')) interval *= 60.0; else if ((ch == 'h') || (ch == 'H')) interval *= 3600.0; break; case 'I': reqval = 1; ident[0] = '-'; strncpy(&ident[1], getoptvalp(argv, 2, reqval, &skiparg), 40); ident[41] = '\0'; break; case 'F': reverse = 1; break; case 'b': reqval = 0; cp1 = getoptvalp(argv, 2, reqval, &skiparg); if (*cp1) { if (isalpha((int)(*cp1))) brief = 1; else brief = atoi(cp1); if (strchr(cp1, 'r')) brief |= BRIEF_RETRANS_STREAMS; if (strchr(cp1, 'c')) { #if defined(linux) brief |= BRIEF_CWND_STREAMS; #else fprintf(stderr, "\"-bc\" option not supported for non-Linux\n"); fflush(stderr); #endif } } else brief = 1; break; case 'S': if (strchr(&argv[0][2], 'P')) pass_ctlport = 1; trans = 0; clientserver = 1; brief = 0; verbose = 1; break; case '1': oneshot = 1; trans = 0; clientserver = 1; brief = 0; verbose = 1; break; case 'V': fprintf(stdout, "nuttcp-%d.%d.%d%s\n", vers_major, vers_minor, vers_delta, beta ? BETA_STR : ""); exit(0); case 'f': if (strcmp(&argv[0][2], "xmitstats") == 0) format |= XMITSTATS; else if (strcmp(&argv[0][2], "debuginterval") == 0) format |= DEBUGINTERVAL; else if (strcmp(&argv[0][2], "runningtotal") == 0) format |= RUNNINGTOTAL; else if (strcmp(&argv[0][2], "-percentloss") == 0) format |= NOPERCENTLOSS; else if (strcmp(&argv[0][2], "-drops") == 0) format |= NODROPS; else if (strcmp(&argv[0][2], "-retrans") == 0) { format |= NORETRANS; format |= NOCWND; } else if (strcmp(&argv[0][2], "debugretrans") == 0) format |= DEBUGRETRANS; else if (strcmp(&argv[0][2], "-cwnd") == 0) format |= NOCWND; else if (strcmp(&argv[0][2], "debugpoll") == 0) format |= DEBUGPOLL; else if (strcmp(&argv[0][2], "debugmtu") == 0) format |= DEBUGMTU; else if (strcmp(&argv[0][2], "debugjitter") == 0) format |= DEBUGJITTER; #if defined(linux) else if (strcmp(&argv[0][2], "debugirate") == 0) format |= DEBUGIRATE; #endif else if (strcmp(&argv[0][2], "parse") == 0) format |= PARSE; else if (strcmp(&argv[0][2], "-beta") == 0) format |= NOBETAMSG; /* below is for compatibility with 6.0.x beta */ else if (strcmp(&argv[0][2], "rtt") == 0) format |= WANTRTT; else if (strcmp(&argv[0][2], "-rtt") == 0) format &= ~WANTRTT; else { if (argv[0][2]) { fprintf(stderr, "invalid format option \"%s\"\n", &argv[0][2]); fflush(stderr); exit(1); } else { fprintf(stderr, "invalid null format option\n"); fprintf(stderr, "perhaps the \"-F\" flip option was intended\n"); fflush(stderr); exit(1); } } break; case 'x': reqval = 1; if (argv[0][2] == 't') { traceroute = 1; brief = 1; } #ifdef HAVE_SETPRIO else if (argv[0][2] == 'P') { priority = atoi(getoptvalp(argv, 3, reqval, &skiparg)); } #endif #ifdef HAVE_SETAFFINITY else if (argv[0][2] == 'c') { reqval = 1; if (argv[0][3] == 's') { cp1 = getoptvalp(argv, 4, reqval, &skiparg); srvr_affinity = atoi(cp1); if (srvr_affinity < 0) { fprintf(stderr, "invalid srvr_affinity " "= %d\n", srvr_affinity); fflush(stderr); exit(1); } } else { cp1 = getoptvalp(argv, 3, reqval, &skiparg); affinity = atoi(cp1); if ((affinity < 0) || (affinity >= CPU_SETSIZE)) { fprintf(stderr, "invalid affinity " "= %d\n", affinity); fflush(stderr); exit(1); } if ((cp2 = strchr(cp1, '/'))) { srvr_affinity = atoi(cp2 + 1); if (srvr_affinity < 0) { fprintf(stderr, "invalid " "srvr_affinity " "= %d\n", srvr_affinity); fflush(stderr); exit(1); } } } } #endif else { if (argv[0][2]) { fprintf(stderr, "invalid x option \"%s\"\n", &argv[0][2]); fflush(stderr); exit(1); } else { fprintf(stderr, "invalid null x option\n"); fflush(stderr); exit(1); } } break; case '3': thirdparty = 1; break; case 'm': reqval = 0; if (argv[0][2] == 'a') { ssm = 0; cp1 = getoptvalp(argv, 3, reqval, &skiparg); } else if (argv[0][2] == 's') { #ifdef MCAST_JOIN_SOURCE_GROUP ssm = 1; cp1 = getoptvalp(argv, 3, reqval, &skiparg); #else fprintf(stderr, "This system does not support SSM\n"); fflush(stderr); exit(1); #endif } else { cp1 = getoptvalp(argv, 2, reqval, &skiparg); } if (*cp1) mc_param = atoi(cp1); else mc_param = 1; if ((mc_param < 1) || (mc_param > 255)) { fprintf(stderr, "invalid multicast ttl = %d\n", mc_param); fflush(stderr); exit(1); } multicast = mc_param; break; case 'g': reqval = 1; mc_addr = getoptvalp(argv, 2, reqval, &skiparg); break; case 'M': reqval = 1; datamss = atoi(getoptvalp(argv, 2, reqval, &skiparg)); if (datamss < 0) { fprintf(stderr, "invalid datamss = %d\n", datamss); fflush(stderr); exit(1); } break; case 'c': reqval = 1; cp1 = getoptvalp(argv, 2, reqval, &skiparg); tos = strtol(cp1, NULL, 0); if (*cp1) ch = *(cp1 + strlen(cp1) - 1); else ch = '\0'; if ((ch == 'p') || (ch == 'P')) { /* Precedence */ if (tos > 7) { fprintf(stderr, "invalid precedence = %d\n", tos); fflush(stderr); exit(1); } tos <<= 5; } else if ((ch != 't') && (ch != 'T')) { /* DSCP */ if (tos > 63) { fprintf(stderr, "invalid dscp = %d\n", tos); fflush(stderr); exit(1); } tos <<= 2; } if (tos > 255) { fprintf(stderr, "invalid tos = %d\n", tos); fflush(stderr); exit(1); } break; case 'a': retry_server = 1; break; case '-': if (strcmp(&argv[0][2], "nofork") == 0) { nofork=1; } else if (strcmp(&argv[0][2], "no3rdparty") == 0) { no3rd=1; } else if (strcmp(&argv[0][2], "idle-data-timeout") == 0) { if ((cp1 = strchr(argv[1], '/'))) { if (strchr(cp1 + 1, '/')) { if (sscanf(argv[1], "%lf/%lf/%lf", &idle_data_min, &default_idle_data, &idle_data_max) != 3) { fprintf(stderr, "error scanning idle-data-timeout parameter = %s\n", argv[1]); fflush(stderr); exit(1); } if (idle_data_min <= 0.0) { fprintf(stderr, "invalid value for idle-data-timeout minimum = %f\n", idle_data_min); fflush(stderr); exit(1); } if (default_idle_data <= 0.0) { fprintf(stderr, "invalid value for idle-data-timeout default = %f\n", default_idle_data); fflush(stderr); exit(1); } if (idle_data_max <= 0.0) { fprintf(stderr, "invalid value for idle-data-timeout maximum = %f\n", idle_data_max); fflush(stderr); exit(1); } if (idle_data_max < idle_data_min) { fprintf(stderr, "error: idle-data-timeout maximum of %f < minimum of %f\n", idle_data_max, idle_data_min); fflush(stderr); exit(1); } } else { fprintf(stderr, "invalid idle-data-timeout parameter = %s\n", argv[1]); fflush(stderr); exit(1); } } else { sscanf(argv[1], "%lf", &idle_data_min); if (idle_data_min <= 0.0) { fprintf(stderr, "invalid value for idle-data-timeout = %f\n", idle_data_min); fflush(stderr); exit(1); } idle_data_max = idle_data_min; default_idle_data = idle_data_min; } argv++; argc--; } else if (strcmp(&argv[0][2], "single-threaded") == 0) { single_threaded=1; } else if (strcmp(&argv[0][2], "packet-burst") == 0) { maxburst = atoi(argv[1]); if (maxburst <= 0) { fprintf(stderr, "invalid maxburst = %d\n", maxburst); fflush(stderr); exit(1); } argv++; argc--; } #ifdef IPV6_V6ONLY else if (strcmp(&argv[0][2], "disable-v4-mapped") == 0) { v4mapped=0; } else if (strcmp(&argv[0][2], "enable-v4-mapped") == 0) { v4mapped=1; } #endif else { goto usage; } break; case 'h': default: goto usage; } argv++; argc--; if (skiparg) { argv++; argc--; } } if (argc > 2) goto usage; if (trans && (argc < 1)) goto usage; if (clientserver && (argc != 0)) goto usage; if (!clientserver && !trans && (argc < 1)) { fprintf(stderr, "nuttcp: Warning: Using obsolete \"classic\" mode:\n"); fprintf(stderr, " Automatically switching to " "oneshot server mode " "(\"nuttcp -1\")\n"); oneshot = 1; trans = 0; clientserver = 1; brief = 0; verbose = 1; } host3 = NULL; if (argc == 2) { host3 = argv[1]; if (strlen(host3) > HOSTNAMELEN) { fprintf(stderr, "3rd party host '%s' too long\n", host3); fflush(stderr); exit(1); } cp1 = host3; while (*cp1) { if (!isalnum((int)(*cp1)) && (*cp1 != '-') && (*cp1 != '.') && (*cp1 != ':') && (*cp1 != '/') && (*cp1 != '+') && (*cp1 != '=')) { fprintf(stderr, "invalid 3rd party host '%s'\n", host3); fflush(stderr); exit(1); } cp1++; } } if (multicast) { udp = 1; if (!buflenopt) buflen = DEFAULT_MC_UDPBUFLEN; nstream = 1; } if (mc_addr && !multicast) { fprintf(stderr, "can't use \"-g\" option for non-multicast\n"); fflush(stderr); exit(1); } if (mc_addr && !*mc_addr) { fprintf(stderr, "no multicast IP address specified for " "\"-g\" option\n"); fflush(stderr); exit(1); } #ifdef AF_INET6 if (!inet_pton(AF_INET6, HI_MC6, &hi_mc6)) { err("inet_pton"); } if (!inet_pton(AF_INET6, HI_MC6_ASM, &hi_mc6_asm)) { err("inet_pton"); } #endif if (udp && !haverateopt) rate = DEFAULT_UDP_RATE; bzero((char *)&frominet, sizeof(frominet)); bzero((char *)&clientaddr, sizeof(clientaddr)); #ifdef AF_INET6 bzero((char *)&clientaddr6, sizeof(clientaddr6)); clientscope6 = 0; #endif if (!nbuf) { if (timeout == 0.0) { if (sinkmode) { timeout = DEFAULT_TIMEOUT; itimer.it_value.tv_sec = timeout; itimer.it_value.tv_usec = (timeout - itimer.it_value.tv_sec) *1000000; } nbuf = INT_MAX; } } if (srvrwin == -1) { srvrwin = sendwin; } if ((argc == 0) && !explicitaf) { domain = PF_INET; af = AF_INET; } if (multilink) { if (nstream == 1) { fprintf(stderr, "Warning: multilink mode not meaningful for a single stream\n"); fflush(stderr); } } if (argc >= 1) { host = argv[0]; if ((cp1 = strchr(host, '+'))) { *cp1++ = '\0'; if (*cp1) stride = cp1; } if (multilink) { if (stride) { fprintf(stderr, "don't use both multilink and address stride\n"); fflush(stderr); exit(1); } if ((cp1 = strchr(host, '/')) && strchr(cp1 + 1, '/')) { fprintf(stderr, "multilink mode not compatible with multiple hosts %s\n", host); fflush(stderr); exit(1); } } hostaddr = NULL; implicit_hostaddr = 0; if ((cp1 = strchr(host, '='))) { *cp1++ = '\0'; if (strchr(cp1, '/')) { fprintf(stderr, "host=addr format not supported for multiple control/data paths\n"); fflush(stderr); exit(1); } if (*cp1) { if (*cp1 == '=') { implicit_hostaddr = 1; cp1++; } hostaddr = cp1; if (!implicit_hostaddr) host = hostaddr; } } stream_idx = 0; res[0] = NULL; cp1 = host; if (host[strlen(host) - 1] == '/') { fprintf(stderr, "bad hostname or address: trailing '/' not allowed: %s\n", host); fflush(stderr); exit(1); } if (strchr(host, '/') && !trans && !reverse) { fprintf(stderr, "multiple control/data paths not supported for receive\n"); fflush(stderr); exit(1); } if (strchr(host, '/') && trans && reverse) { fprintf(stderr, "multiple control/data paths not supported for flipped transmit\n"); fflush(stderr); exit(1); } if (host[0] == '/') { host++; cp1++; stream_idx = 1; } else if ((cp2 = strchr(host, '/'))) { host = cp2 + 1; } while (stream_idx <= nstream) { bzero(&hints, sizeof(hints)); res[stream_idx] = NULL; if (explicitaf) hints.ai_family = af; if (udp) hints.ai_socktype = SOCK_DGRAM; else hints.ai_socktype = SOCK_STREAM; if ((cp2 = strchr(cp1, '/'))) { if (stream_idx == nstream) { fprintf(stderr, "bad hostname or address: too many data paths for nstream=%d: %s\n", nstream, argv[0]); fflush(stderr); exit(1); } *cp2 = '\0'; } if (!(multilink && (stream_idx > 1)) && (error_num = getaddrinfo(cp1, NULL, &hints, &res[stream_idx]))) { if (implicit_hostaddr && hostaddr) { if (res[stream_idx]) { freeaddrinfo(res[stream_idx]); res[stream_idx] = NULL; } error_num = getaddrinfo(hostaddr, NULL, &hints, &res[stream_idx]); } if (error_num) { if (cp2) *cp2++ = '/'; if (hostaddr) { if (implicit_hostaddr) *(hostaddr - 2) = '='; else *(hostaddr - 1) = '='; } fprintf(stderr, "bad hostname or address: %s: %s\n", gai_strerror(error_num), argv[0]); fflush(stderr); exit(1); } if (implicit_hostaddr && hostaddr && (stream_idx == 1)) { if (stride) *(stride - 1) = '+'; cp3 = hostaddr; while (*cp3) { *(cp3 - 1) = *cp3; cp3++; } *(cp3 - 1) = '\0'; hostaddr--; implicit_hostaddr = 0; if (stride) { stride--; *(stride - 1) = '\0'; } } } else if (multilink && (stream_idx > 1)) { if (res[stream_idx - 1]->ai_next) res[stream_idx] = res[stream_idx - 1]->ai_next; else res[stream_idx] = res[1]; } else if (!(multilink && (stream_idx > 1)) && implicit_hostaddr && hostaddr) { if (stride) { strcat(host, "+"); strncat(host, stride, ADDRSTRLEN); *(hostaddr - 2) = '\0'; stride = hostaddr - 1; } hostaddr = NULL; } af = res[stream_idx]->ai_family; /* * At the moment PF_ matches AF_ but are maintained seperate and the socket * call is supposed to be PF_ * * For now we set domain from the address family we looked up, but if these * ever get changed to not match some code will have to go here to find the * domain appropriate for the family */ domain = af; stream_idx++; if (cp2) { *cp2++ = '/'; cp1 = cp2; } else cp1 = host; } if (!res[0]) { if ((cp1 = strchr(host, '/'))) *cp1 = '\0'; if ((error_num = getaddrinfo(host, NULL, &hints, &res[0]))) { if (cp1) *cp1++ = '/'; fprintf(stderr, "bad hostname or address: %s: %s\n", gai_strerror(error_num), argv[0]); fflush(stderr); exit(1); } af = res[0]->ai_family; /* see previous comment about domain */ domain = af; if (cp1) *cp1 = '/'; } if (hostaddr) { host = argv[0]; if (implicit_hostaddr) *(hostaddr - 2) = '='; else *(hostaddr - 1) = '='; } } ipad_stride.ip32 = 0; if (stride) { if (strlen(stride) >= ADDRSTRLEN) { fprintf(stderr, "address stride '%s' too long\n", stride); fflush(stderr); exit(1); } if (nstream == 1) { fprintf(stderr, "Warning: stride %s not meaningful for a single stream\n", stride); fflush(stderr); } if (udp) { fprintf(stderr, "stride %s not valid for UDP\n", stride); fflush(stderr); exit(1); } if ((cp1 = strchr(argv[0], '/')) && strchr(cp1 + 1, '/')) { fprintf(stderr, "stride %s not compatible with multiple hosts %s\n", stride, argv[0]); fflush(stderr); exit(1); } if (af == AF_INET) { if (strchr(stride, '.')) { error_num = inet_pton(AF_INET, stride, ipad_stride.buf); if (error_num == 0) { fprintf(stderr, "stride %s not in correct presentation format\n", stride); fflush(stderr); exit(1); } else if (error_num < 0) err("inet_pton: stride"); } else { ipad_stride.ip32 = atoi(stride); ipad_stride.ip32 = htonl(ipad_stride.ip32); } } else { fprintf(stderr, "stride %s not valid for IPv6\n", stride); fflush(stderr); exit(1); } *(stride - 1) = '+'; } if (host3 && !strchr(host3, '=') && !strchr(host3, '/')) { cp1 = strchr(host3, '+'); if (cp1) { if (strlen(cp1 + 1) >= ADDRSTRLEN) { fprintf(stderr, "3rd party address stride '%s' too long\n", cp1 + 1); fflush(stderr); exit(1); } *cp1 = '\0'; } if (inet_pton(af, host3, &dummy) != 1) { bzero(&hints, sizeof(hints)); hints.ai_family = af; if (udp) hints.ai_socktype = SOCK_DGRAM; else hints.ai_socktype = SOCK_STREAM; host3res = NULL; error_num = getaddrinfo(host3, NULL, &hints, &host3res); if (error_num == 0) { nameinfo_flags = NI_NUMERICHOST; error_num = getnameinfo(host3res->ai_addr, host3res->ai_addrlen, host3addr, ADDRSTRLEN, NULL, 0, nameinfo_flags); } if (host3res) { freeaddrinfo(host3res); host3res = NULL; } if (error_num == 0) { strncpy(host3buf, host3, HOSTNAMELEN); strcat(host3buf, "=="); strncat(host3buf, host3addr, ADDRSTRLEN); if (cp1) { strcat(host3buf, "+"); strncat(host3buf, cp1 + 1, ADDRSTRLEN); } host3 = host3buf; } } if (cp1) *cp1 = '+'; } if (!port) { if (af == AF_INET) { if ((sp = getservbyname( "nuttcp-data", "tcp" ))) port = ntohs(sp->s_port); else port = DEFAULT_PORT; } #ifdef AF_INET6 else if (af == AF_INET6) { if ((sp = getservbyname( "nuttcp6-data", "tcp" ))) port = ntohs(sp->s_port); else { if ((sp = getservbyname( "nuttcp-data", "tcp" ))) port = ntohs(sp->s_port); else port = DEFAULT_PORT; } } #endif else { err("unsupported AF"); } } if (!ctlport) { if (af == AF_INET) { if ((sp = getservbyname( "nuttcp", "tcp" ))) ctlport = ntohs(sp->s_port); else ctlport = DEFAULT_CTLPORT; } #ifdef AF_INET6 else if (af == AF_INET6) { if ((sp = getservbyname( "nuttcp6", "tcp" ))) ctlport = ntohs(sp->s_port); else { if ((sp = getservbyname( "nuttcp", "tcp" ))) ctlport = ntohs(sp->s_port); else ctlport = DEFAULT_CTLPORT; } } #endif else { err("unsupported AF"); } } if ((port < 1024) || ((port + nstream - 1) > 65535)) { fprintf(stderr, "invalid port/nstream = %d/%d\n", port, nstream); fflush(stderr); exit(1); } if ((ctlport >= port) && (ctlport <= (port + nstream - 1))) { fprintf(stderr, "ctlport = %d overlaps port/nstream = %d/%d\n", ctlport, port, nstream); fflush(stderr); exit(1); } if (timeout && (interval >= timeout)) { fprintf(stderr, "ignoring interval=%f which is greater than or equal timeout=%f\n", interval, timeout); fflush(stderr); interval = 0; } if (iratesss) { if (udp) { fprintf(stderr, "ignoring smoothed slow start option for udp transfer\n"); fflush(stderr); iratesss = 0; } if (maxburst > 1) { fprintf(stderr, "ignoring maxburst option with smoothed slow start option\n"); fflush(stderr); maxburst = 1; } if (nstream > 1) { fprintf(stderr, "ignoring smoothed slow start option for multiple streams\n"); fflush(stderr); iratesss = 0; } if (format & NORETRANS) { fprintf(stderr, "can't do smoothed slow start if no retransmission info\n"); fflush(stderr); iratesss = 0; } if (!(format & NORETRANS) && (format & NOCWND)) { fprintf(stderr, "can't do smoothed slow start if no congestion window info\n"); fflush(stderr); iratesss = 0; } } if (clientserver) { if (trans) { fprintf(stderr, "server mode only allowed for receiver\n"); goto usage; } udp = 0; start_idx = 0; ident[0] = '\0'; if (af == AF_INET) { union peer46 { struct sockaddr_in peer4; #ifdef AF_INET6 struct sockaddr_in6 peer6; #endif } peer; socklen_t peerlen = sizeof(peer); if (getpeername(0, (struct sockaddr *)&peer, &peerlen) == 0) { #ifndef AF_INET6 if (peer.peer4.sin_family == AF_INET) #else if ((peer.peer4.sin_family == AF_INET) || (peer.peer6.sin6_family == AF_INET6)) #endif { #ifdef AF_INET6 if (!explicitaf && (peer.peer6.sin6_family == AF_INET6)) { af = AF_INET6; domain = af; clientaddr6 = peer.peer6.sin6_addr; clientscope6 = peer.peer6.sin6_scope_id; client_ipaddr.ss.ss_family = AF_INET6; client_ipaddr.sin6.sin6_addr = clientaddr6; } else { clientaddr = peer.peer4.sin_addr; client_ipaddr.ss.ss_family = AF_INET; client_ipaddr.sin.sin_addr = clientaddr; } #else clientaddr = peer.peer4.sin_addr; client_ipaddr.ss.ss_family = AF_INET; client_ipaddr.sin.sin_addr = clientaddr; #endif inetd = 1; oneshot = 1; start_idx = 1; } } } #ifdef AF_INET6 else if (af == AF_INET6) { struct sockaddr_in6 peer; socklen_t peerlen = sizeof(peer); if (getpeername(0, (struct sockaddr *)&peer, &peerlen) == 0) { if ((peer.sin6_family == AF_INET) || (peer.sin6_family == AF_INET6)) { clientaddr6 = peer.sin6_addr; clientscope6 = peer.sin6_scope_id; client_ipaddr.ss.ss_family = AF_INET6; client_ipaddr.sin6.sin6_addr = clientaddr6; inetd = 1; oneshot = 1; start_idx = 1; } } } #endif else { err("unsupported AF"); } } if (clientserver && !inetd && !oneshot && !sinkmode) { fprintf(stderr, "option \"-s\" invalid with \"-S\" server mode\n"); fprintf(stderr, "option \"-s\" can be used with \"-1\" oneshot server mode\n"); fflush(stderr); exit(1); } #ifdef HAVE_SETPRIO if (priority) { if (setpriority(PRIO_PROCESS, 0, priority) != 0) err("couldn't change priority"); } #endif #ifdef HAVE_SETAFFINITY if ((affinity >= 0) && !host3) { if ((ncores = sysconf(_SC_NPROCESSORS_CONF)) <= 0) err("sysconf: couldn't get _SC_NPROCESSORS_CONF"); CPU_ZERO(&cpu_set); CPU_SET(affinity, &cpu_set); if (sched_setaffinity(0, sizeof(cpu_set_t), &cpu_set) != 0) err("couldn't change CPU affinity"); } #endif if (argc >= 1) { start_idx = 0; client = 1; clientserver = 1; } if (clientserver && !client && srcctlport) { fprintf(stderr, "can't specify source control port in server mode\n"); fflush(stderr); exit(1); } if (!host3 && clientserver && client && (ssm < 0)) { if (af == AF_INET) { ssm = 0; } #ifdef AF_INET6 else if (af == AF_INET6) { #ifdef MCAST_JOIN_SOURCE_GROUP ssm = 1; #else ssm = 0; #endif } #endif } mc_af = af; if (!host3 && clientserver && client && mc_addr) { bzero(&hints, sizeof(hints)); if (explicitaf) hints.ai_family = af; if (udp) hints.ai_socktype = SOCK_DGRAM; else hints.ai_socktype = SOCK_STREAM; mcres = NULL; error_num = getaddrinfo(mc_addr, NULL, &hints, &mcres); if (error_num) { fprintf(stderr, "getaddrinfo: " "bad multicast IP address: %s: %s\n", mc_addr, gai_strerror(error_num)); fflush(stderr); exit(1); } nameinfo_flags = NI_NUMERICHOST; error_num = getnameinfo(mcres->ai_addr, mcres->ai_addrlen, mcgaddr, ADDRSTRLEN, NULL, 0, nameinfo_flags); if (error_num) { fprintf(stderr, "getnameinfo: " "bad multicast IP address: %s: %s\n", mc_addr, gai_strerror(error_num)); fflush(stderr); exit(1); } mc_addr = mcgaddr; if (mcres->ai_family == AF_INET) { struct sockaddr_in *group; struct in_addr ipv4_mcaddr; group = (struct sockaddr_in *)mcres->ai_addr; bcopy((char *)&(group->sin_addr), (char *)&ipv4_mcaddr, sizeof(struct in_addr)); if (ssm) { if (((htonl(ipv4_mcaddr.s_addr) & 0xFF000000) != (HI_MC_SSM << 24))) { fprintf(stderr, "bad SSM multicast " "IP address: %s: " "use 232.x.y.z\n", mcgaddr); fflush(stderr); exit(1); } } else { if (((htonl(ipv4_mcaddr.s_addr) & 0xFF000000) != (HI_MC << 24))) { fprintf(stderr, "bad ASM multicast " "IP address: %s: " "use 231.x.y.z\n", mcgaddr); fflush(stderr); exit(1); } } } #ifdef AF_INET6 if (mcres->ai_family == AF_INET6) { struct sockaddr_in6 *group; group = (struct sockaddr_in6 *)mcres->ai_addr; if (ssm) { if ((bcmp((char *)&(group->sin6_addr), (char *)&hi_mc6, HI_MC6_LEN - 1) != 0) || (group->sin6_addr.s6_addr[HI_MC6_LEN - 1] < 0x80)) { fprintf(stderr, "bad SSM multicast IP address: " "%s: use ff3e::[8-f]xxx:yyyy\n", mcgaddr); fflush(stderr); exit(1); } } else { if ((bcmp((char *)&(group->sin6_addr), (char *)&hi_mc6_asm, HI_MC6_ASM_LEN) != 0)) { fprintf(stderr, "bad ASM multicast IP address: " "%s: use ff2e::wwww:xxxx:" "yyyy:zzzz\n", mcgaddr); fflush(stderr); exit(1); } } } #endif mc_af = mcres->ai_family; } if (irate < 0) { if (do_jitter) irate = 1; else irate = 0; } if (do_jitter && (rate == MAXRATE)) { fprintf(stderr, "jitter option not supported for " "unlimited rate\n"); fflush(stderr); exit(1); } if (do_jitter && !irate) { fprintf(stderr, "jitter option requires" " \"-Ri\" instantaneous rate limit option\n"); fflush(stderr); exit(1); } if (interval && !clientserver) { fprintf(stderr, "interval option only supported for client/server mode\n"); fflush(stderr); exit(1); } if (reverse && !clientserver) { fprintf(stderr, "flip option only supported for client/server mode\n"); fflush(stderr); exit(1); } if (reverse && udp) { fprintf(stderr, "flip option not supported for UDP\n"); fflush(stderr); exit(1); } if (client && !sinkmode && udp) { fprintf(stderr, "Warning: UDP transfers unreliable for non-sinkmode - use at own risk\n"); fflush(stderr); } if (traceroute) { nstream = 1; if (!clientserver) { fprintf(stderr, "traceroute option only supported for client/server mode\n"); fflush(stderr); exit(1); } } if (host3) { if (!clientserver) { fprintf(stderr, "3rd party nuttcp only supported for client/server mode\n"); fflush(stderr); exit(1); } } if (udp && (buflen < 5)) { fprintf(stderr, "UDP buflen = %d < 5, set to 5\n", buflen); buflen = 5; /* send more than the sentinel size */ } if (udp && (buflen > MAXUDPBUFLEN)) { fprintf(stderr, "UDP buflen = %d > MAXUDPBUFLEN, set to %d\n", buflen, MAXUDPBUFLEN); buflen = MAXUDPBUFLEN; } if (nbuf_bytes && !host3 && !traceroute) { nbuf /= buflen; } if ((rate != MAXRATE) && rate_pps && !host3 && !traceroute) { uint64_t llrate = rate; llrate *= ((double)buflen * 8 / 1000); rate = llrate; } if (udp && interval) { if (buflen >= 32) udplossinfo = 1; else fprintf(stderr, "Unable to print interval loss information if UDP buflen < 32\n"); } if (udp && (do_jitter & JITTER_IGNORE_OOO)) { if (buflen >= 32) udplossinfo = 1; else fprintf(stderr, "Unable to check out of order when calculating jitter if UDP buflen < 32\n"); } if (!udp && trans) { if (buflen >= 32) { retransinfo = 1; #if defined(linux) cwndinfo = 1; #endif b_flag = 1; } else fprintf(stderr, "Unable to print retransmission information if TCP buflen < 32\n"); } if (udp && do_owd && (buflen < 16)) { fprintf(stderr, "Unable to calculate one-way delay if UDP buflen < 16\n"); } ivers = vers_major*10000 + vers_minor*100 + vers_delta; mallocsize = buflen; if (mallocsize < MINMALLOC) mallocsize = MINMALLOC; #if defined(linux) if (directio) { error_num = posix_memalign((void **)&buf, sysconf(_SC_PAGESIZE), mallocsize); if (error_num) { errno = error_num; err("posix_memalign"); } } else #endif if ((buf = (char *)malloc(mallocsize)) == (char *)NULL) err("malloc"); pattern( buf, buflen ); #ifdef SIGPIPE signal(SIGPIPE, sigpipe); #endif signal(SIGINT, sigint); if (clientserver && client && !thirdparty && beta && !(format & NOBETAMSG) && (do_jitter || do_owd)) { fprintf(stderr, "nuttcp-%d.%d.%d: ", vers_major, vers_minor, vers_delta); fprintf(stderr, "Using beta vers: %s interface/output " "subject to change\n", BETA_FEATURES); fprintf(stderr, " (to suppress this message " "use \"-f-beta\")\n\n"); fflush(stderr); } doit: if (!udp && trans && (format & DEBUGRETRANS)) { sretrans = get_retrans(-1, &tcpinf); fprintf(stdout, "initial system retrans = %d\n", sretrans); } nretrans[0] = 0; cwnd[0] = 0; for ( stream_idx = 1; stream_idx <= nstream; stream_idx++ ) { fd[stream_idx] = -1; nretrans[stream_idx] = 0; cwnd[stream_idx] = 0; } for ( stream_idx = start_idx; stream_idx <= nstream; stream_idx++ ) { if (clientserver && (stream_idx == 1)) { retransinfo = 0; cwndinfo = 0; if (nstream == 1) { send_retrans = 1; read_retrans = 1; send_cwnd = 1; read_cwnd = 1; } do_retrans = 0; do_cwnd = 0; got_0retrans = 0; if (client) { if (udp && !host3 && !traceroute) { ctlconnmss = 0; optlen = sizeof(ctlconnmss); if (getsockopt(fd[0], IPPROTO_TCP, TCP_MAXSEG, (void *)&ctlconnmss, &optlen) < 0) err("get ctlconn maximum segment size didn't work"); if (!ctlconnmss) { ctlconnmss = NON_JUMBO_ETHER_MSS; if (format & DEBUGMTU) { fprintf(stderr, "nuttcp%s%s: Warning: Control connection MSS reported as 0, using %d\n", trans?"-t":"-r", ident, ctlconnmss); fflush(stderr); } } else if (format & DEBUGMTU) fprintf(stderr, "ctlconnmss = %d\n", ctlconnmss); if (buflenopt) { if (buflen > ctlconnmss + TCP_UDP_HDRLEN_DELTA + TCP_TIMESTAMPS_OPTLEN) { if (format & PARSE) fprintf(stderr, "nuttcp%s%s: Warning=\"IP_frags_or_no_data_reception_since_buflen=%d_>_ctlconnmss=%d\"\n", trans?"-t":"-r", ident, buflen, ctlconnmss); else fprintf(stderr, "nuttcp%s%s: Warning: IP frags or no data reception since buflen=%d > ctlconnmss=%d\n", trans?"-t":"-r", ident, buflen, ctlconnmss); fflush(stderr); } } else { while (buflen > ctlconnmss) { buflen >>= 1; if (nbuf_bytes) nbuf <<= 1; if ((rate != MAXRATE) && rate_pps) rate >>= 1; } } if (format & DEBUGMTU) fprintf(stderr, "buflen = %d\n", buflen); } if (!(ctlconn = fdopen(fd[0], "w"))) err("fdopen: ctlconn for writing"); if (!sinkmode) { if (trans) savestdin=dup(0); else { savestdout=dup(1); close(1); dup(2); } } close(0); dup(fd[0]); if (srvr_helo) { fprintf(ctlconn, HELO_FMT, vers_major, vers_minor, vers_delta); fflush(ctlconn); if (!fgets(buf, mallocsize, stdin)) { if ((errno == ECONNRESET) && (num_connect_tries < MAX_CONNECT_TRIES) && retry_server) { /* retry control * connection to server * for certain possibly * transient errors */ fclose(ctlconn); goto doit; } mes("error from server"); fprintf(stderr, "server aborted connection\n"); fflush(stderr); exit(1); } if (sscanf(buf, HELO_FMT, &rvers_major, &rvers_minor, &rvers_delta) < 3) { rvers_major = 0; rvers_minor = 0; rvers_delta = 0; srvr_helo = 0; while (fgets(buf, mallocsize, stdin)) { if (strncmp(buf, "KO", 2) == 0) break; } fclose(ctlconn); goto doit; } irvers = rvers_major*10000 + rvers_minor*100 + rvers_delta; } if (host3 && nbuf_bytes && (irvers < 50501)) nbuf /= buflen; if (host3 && (rate != MAXRATE) && rate_pps && (irvers < 50501)) { uint64_t llrate = rate; llrate *= ((double)buflen * 8 / 1000); rate = llrate; } if (host3 && !buflenopt && (irvers >= 50302)) buflen = 0; fprintf(ctlconn, "buflen = %d, nbuf = %llu, win = %d, nstream = %d, rate = %lu, port = %hu, trans = %d, braindead = %d", buflen, nbuf, srvrwin, nstream, rate, port, trans, braindead); if (irvers >= 30200) fprintf(ctlconn, ", timeout = %f", timeout); else { timeout_sec = timeout; if (itimer.it_value.tv_usec) timeout_sec++; fprintf(ctlconn, ", timeout = %ld", timeout_sec); if (!trans && itimer.it_value.tv_usec && (brief <= 0)) { fprintf(stdout, "nuttcp-r%s: transmit timeout value rounded up to %ld second%s for old server\n", ident, timeout_sec, (timeout_sec == 1)?"":"s"); } } fprintf(ctlconn, ", udp = %d, vers = %d.%d.%d", udp, vers_major, vers_minor, vers_delta); if (irvers >= 30302) fprintf(ctlconn, ", interval = %f", interval); else { if (interval) { fprintf(stdout, "nuttcp%s%s: interval option not supported by server version %d.%d.%d, need >= 3.3.2\n", trans?"-t":"-r", ident, rvers_major, rvers_minor, rvers_delta); fflush(stdout); interval = 0.0; abortconn = 1; } } if (irvers >= 30401) fprintf(ctlconn, ", reverse = %d", reverse); else { if (reverse) { fprintf(stdout, "nuttcp%s%s: flip option not supported by server version %d.%d.%d, need >= 3.4.1\n", trans?"-t":"-r", ident, rvers_major, rvers_minor, rvers_delta); fflush(stdout); reverse = 0; abortconn = 1; } } if (irvers >= 30501) fprintf(ctlconn, ", format = %d", format); else { if (format) { fprintf(stdout, "nuttcp%s%s: format option not supported by server version %d.%d.%d, need >= 3.5.1\n", trans?"-t":"-r", ident, rvers_major, rvers_minor, rvers_delta); fflush(stdout); format = 0; } } if (irvers >= 30601) { fprintf(ctlconn, ", traceroute = %d", traceroute); if (traceroute) skip_data = 1; fprintf(ctlconn, ", irate = %d", irate); } else { if (traceroute) { fprintf(stdout, "nuttcp%s%s: traceroute option not supported by server version %d.%d.%d, need >= 3.6.1\n", trans?"-t":"-r", ident, rvers_major, rvers_minor, rvers_delta); fflush(stdout); traceroute = 0; abortconn = 1; } if (irate && !trans) { fprintf(stdout, "nuttcp%s%s: instantaneous rate option not supported by server version %d.%d.%d, need >= 3.6.1\n", trans?"-t":"-r", ident, rvers_major, rvers_minor, rvers_delta); fflush(stdout); irate = 0; } } if (srvrwin && udp && (irvers < 30602)) { fprintf(stdout, "nuttcp%s%s: server version %d.%d.%d ignores UDP window parameter, need >= 3.6.2\n", trans?"-t":"-r", ident, rvers_major, rvers_minor, rvers_delta); fflush(stdout); } if ((irvers < 40101) && (format & PARSE)) { fprintf(stdout, "nuttcp%s%s: \"-fparse\" option not supported by server version %d.%d.%d, need >= 4.1.1\n", trans?"-t":"-r", ident, rvers_major, rvers_minor, rvers_delta); fflush(stdout); format &= ~PARSE; abortconn = 1; } if (irvers >= 50001) { cp1 = NULL; if (host3 && (irvers < 70101) && (cp1 = strchr(host3, '='))) *cp1 = '\0'; fprintf(ctlconn, ", thirdparty = %.*s", HOST3BUFLEN, host3 ? host3 : "_NULL_"); if (host3) { skip_data = 1; fprintf(ctlconn, " , brief3 = %d", brief); } if (cp1) *cp1 = '='; } else { if (host3) { fprintf(stdout, "nuttcp%s%s: 3rd party nuttcp not supported by server version %d.%d.%d, need >= 5.0.1\n", trans?"-t":"-r", ident, rvers_major, rvers_minor, rvers_delta); fflush(stdout); host3 = NULL; abortconn = 1; } } if (host3 && !abortconn) { if (irvers >= 60205) { fprintf(ctlconn, " , ctlport3 = %hu", ctlport3); } else { if (ctlport3) { fprintf(stdout, "nuttcp%s%s: ctlport3 option not supported by server version %d.%d.%d, need >= 6.2.5\n", trans?"-t":"-r", ident, rvers_major, rvers_minor, rvers_delta); fflush(stdout); ctlport3 = 0; abortconn = 1; } } } if (irvers >= 50101) { fprintf(ctlconn, " , multicast = %d", multicast); } else { if (multicast) { fprintf(stdout, "nuttcp%s%s: multicast not supported by server version %d.%d.%d, need >= 5.1.1\n", trans?"-t":"-r", ident, rvers_major, rvers_minor, rvers_delta); fflush(stdout); multicast = 0; abortconn = 1; } } if (irvers >= 60201) { fprintf(ctlconn, " , ssm = %d", ssm); } else { if (multicast && (ssm == 1)) { fprintf(stdout, "nuttcp%s%s: ssm not supported by server version %d.%d.%d, need >= 6.2.1\n", trans?"-t":"-r", ident, rvers_major, rvers_minor, rvers_delta); fflush(stdout); ssm = 0; abortconn = 1; } } if (irvers >= 50201) { fprintf(ctlconn, " , datamss = %d", datamss); } else { if (datamss && !trans) { fprintf(stdout, "nuttcp%s%s: mss option not supported by server version %d.%d.%d, need >= 5.2.1\n", trans?"-t":"-r", ident, rvers_major, rvers_minor, rvers_delta); fflush(stdout); datamss = 0; abortconn = 1; } } if (irvers >= 50301) { fprintf(ctlconn, " , tos = %X", tos); } else { if (tos && !trans) { fprintf(stdout, "nuttcp%s%s: tos option not supported by server version %d.%d.%d, need >= 5.3.1\n", trans?"-t":"-r", ident, rvers_major, rvers_minor, rvers_delta); fflush(stdout); tos = 0; abortconn = 1; } } if (irvers >= 50501) { fprintf(ctlconn, " , nbuf_bytes = %d", nbuf_bytes); fprintf(ctlconn, " , rate_pps = %d", rate_pps); fprintf(ctlconn, " , nodelay = %d", nodelay); } else { if (host3 && udp && nbuf_bytes) { fprintf(stdout, "nuttcp%s%s: Warning: \"-n\" option in bytes for third party not supported\n", trans?"-t":"-r", ident); fprintf(stdout, " Warning: by server version %d.%d.%d, need >= 5.5.1\n", rvers_major, rvers_minor, rvers_delta); fprintf(stdout, " Warning: third party request may not transfer\n"); fprintf(stdout, " Warning: desired number of bytes in some UDP cases\n"); fflush(stdout); nbuf_bytes = 0; } if (host3 && udp && rate_pps) { fprintf(stdout, "nuttcp%s%s: Warning: \"-R\" option in pps for third party not supported\n", trans?"-t":"-r", ident); fprintf(stdout, " Warning: by server version %d.%d.%d, need >= 5.5.1\n", rvers_major, rvers_minor, rvers_delta); fprintf(stdout, " Warning: third party request may not produce\n"); fprintf(stdout, " Warning: desired pps rate in some UDP cases\n"); fflush(stdout); rate_pps = 0; } if (nodelay && !trans) { fprintf(stdout, "nuttcp%s%s: TCP_NODELAY opt not supported by server version %d.%d.%d, need >= 5.5.1\n", trans?"-t":"-r", ident, rvers_major, rvers_minor, rvers_delta); fflush(stdout); nodelay = 0; abortconn = 1; } } if (irvers >= 60206) { if (host3) { fprintf(ctlconn, " , affinity = %d", affinity); fprintf(ctlconn, " , srvr_affinity = %d", srvr_affinity); } else { fprintf(ctlconn, " , affinity = %d", srvr_affinity); } } else { if (srvr_affinity >= 0) { fprintf(stdout, "nuttcp%s%s: affinity option not supported by server version %d.%d.%d, need >= 6.2.6\n", trans?"-t":"-r", ident, rvers_major, rvers_minor, rvers_delta); fflush(stdout); srvr_affinity = -1; abortconn = 1; } } if (irvers >= 60207) { fprintf(ctlconn, " , maxburst = %d", maxburst); } else { if ((maxburst > 1) && (!trans || host3)) { fprintf(stdout, "nuttcp%s%s: packet burst not supported by server version %d.%d.%d, need >= 6.2.7\n", trans?"-t":"-r", ident, rvers_major, rvers_minor, rvers_delta); fflush(stdout); maxburst = 1; abortconn = 1; } } if (irvers >= 70001) { fprintf(ctlconn, " , do_jitter = %d", do_jitter); } else { if (do_jitter && trans) { fprintf(stdout, "nuttcp%s%s: jitter not supported by server version %d.%d.%d, need >= 7.0.1\n", trans?"-t":"-r", ident, rvers_major, rvers_minor, rvers_delta); fflush(stdout); do_jitter = 0; abortconn = 1; } if ((do_jitter & JITTER_IGNORE_OOO) && !trans && !(udp && interval)) { udplossinfo = 0; fprintf(stdout, "nuttcp%s%s: Unable to check out of order when calculating jitter\n", trans?"-t":"-r", ident); fprintf(stdout, " due to using older server version %d.%d.%d, need >= 7.0.1\n", rvers_major, rvers_minor, rvers_delta); fflush(stdout); } } if (irvers >= 70001) { fprintf(ctlconn, " , do_owd = %d", do_owd); } else { if (do_owd) { fprintf(stdout, "nuttcp%s%s: owd not supported by server version %d.%d.%d, need >= 7.0.1\n", trans?"-t":"-r", ident, rvers_major, rvers_minor, rvers_delta); fflush(stdout); do_owd = 0; abortconn = 1; } } if (irvers >= 70101) { fprintf(ctlconn, " , stride = %d", ipad_stride.ip32); } else { if (ipad_stride.ip32) { fprintf(stdout, "nuttcp%s%s: stride not supported by server version %d.%d.%d, need >= 7.1.1\n", trans?"-t":"-r", ident, rvers_major, rvers_minor, rvers_delta); fflush(stdout); ipad_stride.ip32 = 0; abortconn = 1; } } if (irvers >= 70101) { fprintf(ctlconn, " , multilink = %d", multilink); } else { if (multilink) { fprintf(stdout, "nuttcp%s%s: multilink not supported by server version %d.%d.%d, need >= 7.1.1\n", trans?"-t":"-r", ident, rvers_major, rvers_minor, rvers_delta); fflush(stdout); multilink = 0; abortconn = 1; } } if (irvers >= 70201) { fprintf(ctlconn, ", group = %.*s", ADDRSTRLEN, mc_addr ? mc_addr : "_NULL_"); } else { if (mc_addr) { fprintf(stdout, "nuttcp%s%s: multicast group option not supported by server version %d.%d.%d, need >= 7.2.1\n", trans?"-t":"-r", ident, rvers_major, rvers_minor, rvers_delta); fflush(stdout); mc_addr = NULL; abortconn = 1; } } if (irvers >= 70301) { fprintf(ctlconn, " , srcport = %hu", srcport); } else { if (srcport && ((!trans && !reverse) || (trans && reverse) || host3)) { fprintf(stdout, "nuttcp%s%s: source port option not supported by server version %d.%d.%d, need >= 7.3.1\n", trans?"-t":"-r", ident, rvers_major, rvers_minor, rvers_delta); fflush(stdout); srcport = 0; abortconn = 1; } } if (irvers >= 80101) { fprintf(ctlconn, ", iratesss = %d", iratesss); } else { if (iratesss && !trans) { fprintf(stdout, "nuttcp%s%s: smoothed slow start option not supported by server version %d.%d.%d, need >= 8.1.1\n", trans?"-t":"-r", ident, rvers_major, rvers_minor, rvers_delta); fflush(stdout); iratesss = 0; } } fprintf(ctlconn, "\n"); fflush(ctlconn); if (abortconn) { brief = 1; if ((!trans && !reverse) || (trans && reverse)) skip_data = 1; } if (!fgets(buf, mallocsize, stdin)) { mes("error from server"); fprintf(stderr, "server aborted connection\n"); fflush(stderr); exit(1); } if (irvers < 30403) udplossinfo = 0; if (irvers >= 50401) { two_bod = 1; handle_urg = 1; } if (udp || (buflen < 32) || (irvers < 60001)) { if (trans) send_retrans = 0; else read_retrans = 0; } if (udp || (buflen < 32) || (irvers < 80001)) { if (trans) send_cwnd = 0; else read_cwnd = 0; } if (strncmp(buf, "OK", 2) != 0) { mes("error from server"); fprintf(stderr, "server "); while (fgets(buf, mallocsize, stdin)) { if (strncmp(buf, "KO", 2) == 0) break; fputs(buf, stderr); } fflush(stderr); exit(1); } if (sscanf(buf, "OK v%d.%d.%d\n", &rvers_major, &rvers_minor, &rvers_delta) < 3) { rvers_major = 0; rvers_minor = 0; rvers_delta = 0; } irvers = rvers_major*10000 + rvers_minor*100 + rvers_delta; usleep(10000); } else { if (inetd) { ctlconn = stdin; } else { if (!(ctlconn = fdopen(fd[0], "r"))) err("fdopen: ctlconn for reading"); } fflush(stdout); if (!inetd) { /* manually started server */ /* send stdout to client */ savestdout=dup(1); close(1); dup(fd[0]); if (!nofork) { /* send stderr to client */ close(2); dup(1); } } if (!fgets(buf, mallocsize, ctlconn)) { fputs("KO\n", stdout); mes("no nuttcp HELO message"); fputs("KO\n", stdout); goto cleanup; } if (sscanf(buf, HELO_FMT, &rvers_major, &rvers_minor, &rvers_delta) == 3) { fprintf(stdout, HELO_FMT, vers_major, vers_minor, vers_delta); fflush(stdout); if (!fgets(buf, mallocsize, ctlconn)) { fputs("KO\n", stdout); mes("no nuttcp parameters"); fputs("KO\n", stdout); goto cleanup; } } irvers = rvers_major*10000 + rvers_minor*100 + rvers_delta; if (sscanf(buf, "buflen = %d, nbuf = %llu, win = %d, nstream = %d, rate = %lu, port = %hu, trans = %d, braindead = %d, timeout = %lf, udp = %d, vers = %d.%d.%d", &nbuflen, &nbuf, &sendwin, &nstream, &rate, &port, &trans, &braindead, &timeout, &udp, &rvers_major, &rvers_minor, &rvers_delta) < 13) { trans = !trans; fputs("KO\n", stdout); mes("error scanning parameters"); fprintf(stdout, "may be using older client version than server\n"); fputs(buf, stdout); fputs("KO\n", stdout); goto cleanup; } irvers = rvers_major*10000 + rvers_minor*100 + rvers_delta; if (irvers >= 30302) sscanf(strstr(buf, ", interval =") + 13, "%lf", &interval); else interval = 0.0; if (irvers >= 30401) sscanf(strstr(buf, ", reverse =") + 12, "%d", &reverse); else reverse = 0; if (irvers >= 30501) sscanf(strstr(buf, ", format =") + 11, "%d", &format); else format = 0; if (irvers >= 30601) { sscanf(strstr(buf, ", traceroute =") + 15, "%d", &traceroute); if (traceroute) { skip_data = 1; brief = 1; } sscanf(strstr(buf, ", irate =") + 10, "%d", &irate); } else { traceroute = 0; irate = 0; } if (irvers >= 50001) { sprintf(fmt, "%%%ds", HOST3BUFLEN); sscanf(strstr(buf, ", thirdparty =") + 15, fmt, host3buf); host3buf[HOST3BUFLEN] = '\0'; if (strcmp(host3buf, "_NULL_") == 0) host3 = NULL; else host3 = host3buf; if (host3) { if (no3rd) { fputs("KO\n", stdout); fprintf(stdout, "doesn't allow 3rd party nuttcp\n"); fputs("KO\n", stdout); goto cleanup; } cp1 = host3; while (*cp1) { if (!isalnum((int)(*cp1)) && (*cp1 != '-') && (*cp1 != '.') && (*cp1 != ':') && (*cp1 != '/') && (*cp1 != '+') && (*cp1 != '=')) { fputs("KO\n", stdout); mes("invalid 3rd party host"); fprintf(stdout, "3rd party host = '%s'\n", host3); fputs("KO\n", stdout); goto cleanup; } cp1++; } skip_data = 1; brief = 1; sscanf(strstr(buf, ", brief3 =") + 11, "%d", &brief3); } } else { host3 = NULL; } if (host3 && (irvers >= 60205)) { sscanf(strstr(buf, ", ctlport3 =") + 13, "%hu", &ctlport3); } else { ctlport3 = 0; } if (irvers >= 50101) { sscanf(strstr(buf, ", multicast =") + 14, "%d", &mc_param); } else { mc_param = 0; } if (irvers >= 60201) { sscanf(strstr(buf, ", ssm =") + 8, "%d", &ssm); } else { ssm = 0; } #ifndef MCAST_JOIN_SOURCE_GROUP if (mc_param && (ssm == 1)) { fputs("KO\n", stdout); fprintf(stdout, "server does not support ssm\n"); fputs("KO\n", stdout); goto cleanup; } #endif if (irvers >= 50201) { sscanf(strstr(buf, ", datamss =") + 12, "%d", &datamss); } else { datamss = 0; } if (irvers >= 50301) { sscanf(strstr(buf, ", tos =") + 8, "%X", &tos); } else { tos = 0; } if (irvers >= 50501) { sscanf(strstr(buf, ", nbuf_bytes =") + 15, "%d", &nbuf_bytes); sscanf(strstr(buf, ", rate_pps =") + 13, "%d", &rate_pps); sscanf(strstr(buf, ", nodelay =") + 12, "%d", &nodelay); } else { nbuf_bytes = 0; rate_pps = 0; nodelay = 0; } if (irvers >= 60206) { if (host3) { sscanf(strstr(buf, ", affinity =") + 13, "%d", &affinity); sscanf(strstr(buf, ", srvr_affinity =") + 18, "%d", &srvr_affinity); } else { sscanf(strstr(buf, ", affinity =") + 13, "%d", &srvr_affinity); } } else { srvr_affinity = -1; } if (irvers >= 60207) { sscanf(strstr(buf, ", maxburst =") + 13, "%d", &maxburst); } else { maxburst = 1; } if (irvers >= 70001) { sscanf(strstr(buf, ", do_jitter =") + 14, "%d", &do_jitter); } else { do_jitter = 0; } if (irvers >= 70001) { sscanf(strstr(buf, ", do_owd =") + 11, "%d", &do_owd); } else { do_owd = 0; } if (irvers >= 70101) { sscanf(strstr(buf, ", stride =") + 11, "%d", &ipad_stride.ip32); } else { ipad_stride.ip32 = 0; } if (irvers >= 70101) { sscanf(strstr(buf, ", multilink =") + 14, "%d", &multilink); } else { multilink = 0; } if (irvers >= 70201) { sprintf(fmt, "%%%ds", ADDRSTRLEN); sscanf(strstr(buf, ", group =") + 10, fmt, mcgaddr); mcgaddr[ADDRSTRLEN - 1] = '\0'; if (strcmp(mcgaddr, "_NULL_") == 0) mc_addr = NULL; else mc_addr = mcgaddr; if (mc_addr) { cp1 = mc_addr; while (*cp1) { if (!isxdigit((int)(*cp1)) && (*cp1 != '.') && (*cp1 != ':')) { fputs("KO\n", stdout); mes("invalid multicast group"); fprintf(stdout, "multicast group = '%s'\n", mc_addr); fputs("KO\n", stdout); goto cleanup; } cp1++; } } } else { mc_addr = NULL; } if (irvers >= 70301) { sscanf(strstr(buf, ", srcport =") + 12, "%hu", &srcport); } else { srcport = 0; } if (irvers >= 80101) { sscanf(strstr(buf, ", iratesss =") + 13, "%d", &iratesss); } else { iratesss = 0; } #if !defined(linux) iratesss = 0; #endif trans = !trans; if (inetd && !sinkmode) { fputs("KO\n", stdout); mes("option \"-s\" invalid with inetd server"); fputs("KO\n", stdout); goto cleanup; } if (!traceroute && !host3 && (nbuflen != buflen)) { if (nbuflen < 1) { fputs("KO\n", stdout); mes("invalid buflen"); fprintf(stdout, "buflen = %d\n", nbuflen); fputs("KO\n", stdout); goto cleanup; } free(buf); mallocsize = nbuflen; if (mallocsize < MINMALLOC) mallocsize = MINMALLOC; #if defined(linux) if (directio) { error_num = posix_memalign( (void **)&buf, sysconf(_SC_PAGESIZE), mallocsize); if (error_num) { errno = error_num; err("posix_memalign"); } } else #endif if ((buf = (char *)malloc(mallocsize)) == (char *)NULL) err("malloc"); pattern( buf, nbuflen ); } buflen = nbuflen; if (nbuf < 1) { fputs("KO\n", stdout); mes("invalid nbuf"); fprintf(stdout, "nbuf = %llu\n", nbuf); fputs("KO\n", stdout); goto cleanup; } rcvwin = sendwin; if (sendwin < 0) { fputs("KO\n", stdout); mes("invalid win"); fprintf(stdout, "win = %d\n", sendwin); fputs("KO\n", stdout); goto cleanup; } if ((nstream < 1) || (nstream > MAXSTREAM)) { fputs("KO\n", stdout); mes("invalid nstream"); fprintf(stdout, "nstream = %d\n", nstream); fputs("KO\n", stdout); goto cleanup; } if (nstream > 1) { b_flag = 1; send_retrans = 0; read_retrans = 0; send_cwnd = 0; read_cwnd = 0; } if (rate == 0) rate = MAXRATE; if (timeout < 0) { fputs("KO\n", stdout); mes("invalid timeout"); fprintf(stdout, "timeout = %f\n", timeout); fputs("KO\n", stdout); goto cleanup; } itimer.it_value.tv_sec = timeout; itimer.it_value.tv_usec = (timeout - itimer.it_value.tv_sec) *1000000; if ((port < 1024) || ((port + nstream - 1) > 65535)) { fputs("KO\n", stdout); mes("invalid port/nstream"); fprintf(stdout, "port/nstream = %hu/%d\n", port, nstream); fputs("KO\n", stdout); goto cleanup; } if (srcport && (srcport < 1024)) { fputs("KO\n", stdout); mes("invalid srcport"); fprintf(stdout, "srcport = %hu\n", srcport); fputs("KO\n", stdout); goto cleanup; } if ((ctlport >= port) && (ctlport <= (port + nstream - 1))) { fputs("KO\n", stdout); mes("ctlport overlaps port/nstream"); fprintf(stdout, "ctlport = %hu, port/nstream = %hu/%d\n", ctlport, port, nstream); fputs("KO\n", stdout); goto cleanup; } if (iratesss) { if (udp) { fputs("KO\n", stdout); mes("iratesss not allowed for udp"); fputs("KO\n", stdout); goto cleanup; } if (maxburst > 1) { fputs("KO\n", stdout); mes("maxburst > 1 not allowed with iratesss"); fprintf(stdout, "maxburst = %d\n", maxburst); fputs("KO\n", stdout); goto cleanup; } if (nstream > 1) { fputs("KO\n", stdout); mes("nstream > 1 not allowed with iratesss"); fprintf(stdout, "nstream = %d\n", nstream); fputs("KO\n", stdout); goto cleanup; } } if (host3 && ctlport3 && (ctlport3 < 1024)) { fputs("KO\n", stdout); mes("invalid ctlport3"); fprintf(stdout, "ctlport3 = %hu\n", ctlport3); fputs("KO\n", stdout); goto cleanup; } if (interval < 0) { fputs("KO\n", stdout); mes("invalid interval"); fprintf(stdout, "interval = %f\n", interval); fputs("KO\n", stdout); goto cleanup; } if (mc_param) { if ((mc_param < 1) || (mc_param > 255)) { fputs("KO\n", stdout); mes("invalid multicast ttl"); fprintf(stdout, "multicast ttl = %d\n", mc_param); fputs("KO\n", stdout); goto cleanup; } udp = 1; nstream = 1; if (rate == MAXRATE) rate = DEFAULT_UDP_RATE; } multicast = mc_param; if ((!host3 && ((ssm < 0) || (ssm > 1))) || (host3 && ((ssm < -1) || (ssm > 1)))) { fputs("KO\n", stdout); mes("invalid ssm value"); fprintf(stdout, "ssm = %d\n", ssm); fputs("KO\n", stdout); goto cleanup; } if (datamss < 0) { fputs("KO\n", stdout); mes("invalid datamss"); fprintf(stdout, "datamss = %d\n", datamss); fputs("KO\n", stdout); goto cleanup; } if (tos > 255) { fputs("KO\n", stdout); mes("invalid tos"); fprintf(stdout, "tos = %d\n", tos); fputs("KO\n", stdout); goto cleanup; } if (nbuf_bytes < 0) { fputs("KO\n", stdout); mes("invalid nbuf_bytes"); fprintf(stdout, "nbuf_bytes = %d\n", nbuf_bytes); fputs("KO\n", stdout); goto cleanup; } if (rate_pps < 0) { fputs("KO\n", stdout); mes("invalid rate_pps"); fprintf(stdout, "rate_pps = %d\n", rate_pps); fputs("KO\n", stdout); goto cleanup; } if (nodelay < 0) { fputs("KO\n", stdout); mes("invalid nodelay"); fprintf(stdout, "nodelay = %d\n", nodelay); fputs("KO\n", stdout); goto cleanup; } if ((srvr_affinity >= 0) && !host3) { #ifdef HAVE_SETAFFINITY CPU_ZERO(&cpu_set); CPU_SET(srvr_affinity, &cpu_set); if (sched_setaffinity(0, sizeof(cpu_set_t), &cpu_set) != 0) { fputs("KO\n", stdout); mes("couldn't change server " "CPU affinity"); fprintf(stdout, "srvr_affinity = %d\n", srvr_affinity); fputs("KO\n", stdout); goto cleanup; } #else fputs("KO\n", stdout); mes("server doesn't support setting " "CPU affinity"); fprintf(stdout, "srvr_affinity = %d\n", srvr_affinity); fputs("KO\n", stdout); goto cleanup; #endif } if (maxburst < 1) { fputs("KO\n", stdout); mes("invalid maxburst"); fprintf(stdout, "maxburst = %d\n", maxburst); fputs("KO\n", stdout); goto cleanup; } if (do_jitter < 0) { fputs("KO\n", stdout); mes("invalid do_jitter"); fprintf(stdout, "do_jitter = %d\n", do_jitter); fputs("KO\n", stdout); goto cleanup; } if (do_jitter && !udp) { fputs("KO\n", stdout); fprintf(stdout, "jitter option" " not supported for TCP\n"); fputs("KO\n", stdout); goto cleanup; } if (do_jitter && (!rate || (rate == MAXRATE))) { fputs("KO\n", stdout); fprintf(stdout, "jitter option not supported" " for unlimited rate\n"); fputs("KO\n", stdout); goto cleanup; } if (do_jitter && !irate) { fputs("KO\n", stdout); fprintf(stdout, "jitter option requires" " instantaneous rate limit\n"); fputs("KO\n", stdout); goto cleanup; } if (do_owd < 0) { fputs("KO\n", stdout); mes("invalid do_owd"); fprintf(stdout, "do_owd = %d\n", do_owd); fputs("KO\n", stdout); goto cleanup; } if (mc_addr) { error_num = inet_pton(AF_INET, mc_addr, &dummy); #ifdef AF_INET6 if (error_num != 1) error_num = inet_pton(AF_INET6, mc_addr, &dummy); #endif if (error_num != 1) { fputs("KO\n", stdout); mes("invalid multicast group"); fprintf(stdout, "multicast group = '%s'\n", mc_addr); fputs("KO\n", stdout); goto cleanup; } } /* used to send server "OK" here - * now delay sending of server OK until * after successful server bind() - * catches data port collision */ if (udp && (interval || (do_jitter & JITTER_IGNORE_OOO)) && (buflen >= 32) && (irvers >= 30403)) udplossinfo = 1; if (irvers >= 50401) { two_bod = 1; handle_urg = 1; } if (udp || (buflen < 32) || (irvers < 60001)) { if (trans) send_retrans = 0; else read_retrans = 0; } if (udp || (buflen < 32) || (irvers < 80001)) { if (trans) send_cwnd = 0; else read_cwnd = 0; } } } if (clientserver && client && (stream_idx == 1)) { reading_srvr_info = 1; pollfds[0].fd = fileno(ctlconn); pollfds[0].events = POLLIN | POLLPRI; pollfds[0].revents = 0; flags = fcntl(0, F_GETFL, 0); if (flags < 0) err("fcntl 1"); flags |= O_NONBLOCK; if (fcntl(0, F_SETFL, flags) < 0) err("fcntl 2"); itimer.it_value.tv_sec = SRVR_INFO_TIMEOUT; itimer.it_value.tv_usec = 0; itimer.it_interval.tv_sec = 0; itimer.it_interval.tv_usec = 0; setitimer(ITIMER_REAL, &itimer, 0); } if (clientserver && client && (stream_idx == 1) && ((pollst = poll(pollfds, 1, 0)) > 0) && (pollfds[0].revents & (POLLIN | POLLPRI)) && !got_done) { /* check for server output (mainly for server error) */ while (fgets(intervalbuf, sizeof(intervalbuf), stdin)) { setitimer(ITIMER_REAL, &itimer, 0); if (strncmp(intervalbuf, "DONE", 4) == 0) { if (format & DEBUGPOLL) { fprintf(stdout, "got DONE\n"); fflush(stdout); } got_done = 1; intr = 1; break; } else if (strncmp(intervalbuf, "nuttcp-", 7) == 0) { if ((brief <= 0) || strstr(intervalbuf, "Warning") || strstr(intervalbuf, "Error") || strstr(intervalbuf, "Debug")) { if (*ident) { fputs("nuttcp", stdout); fputs(trans ? "-r" : "-t", stdout); fputs(ident, stdout); fputs(intervalbuf + 8, stdout); } else fputs(intervalbuf, stdout); fflush(stdout); } if (strstr(intervalbuf, "Error")) exit(1); } else { if (*ident) fprintf(stdout, "%s: ", ident + 1); fputs(intervalbuf, stdout); fflush(stdout); } } } if (clientserver && client && (stream_idx == 1)) { reading_srvr_info = 0; flags = fcntl(0, F_GETFL, 0); if (flags < 0) err("fcntl 1"); flags &= ~O_NONBLOCK; if (fcntl(0, F_SETFL, flags) < 0) err("fcntl 2"); itimer.it_value.tv_sec = 0; itimer.it_value.tv_usec = 0; setitimer(ITIMER_REAL, &itimer, 0); } if (!client) { if (af == AF_INET) { inet_ntop(af, &clientaddr.s_addr, hostbuf, sizeof(hostbuf)); } #ifdef AF_INET6 else if (af == AF_INET6) { inet_ntop(af, clientaddr6.s6_addr, hostbuf, sizeof(hostbuf)); } #endif host = hostbuf; } if (multilink && !client && ((trans && !reverse) || (!trans && reverse)) && !udp && (stream_idx == 1)) { nameinfo_flags = NI_NAMEREQD; if (getnameinfo((struct sockaddr *)&client_ipaddr, sizeof(struct in6_addr), clientbuf, NI_MAXHOST, NULL, 0, nameinfo_flags) == 0) { bzero(&hints, sizeof(hints)); res[0] = NULL; res[1] = NULL; hints.ai_family = af; if (udp) hints.ai_socktype = SOCK_DGRAM; else hints.ai_socktype = SOCK_STREAM; if (getaddrinfo(clientbuf, NULL, &hints, &res[1]) == 0) { for ( i = 2; i <= nstream; i++ ) { if (res[i - 1]->ai_next) res[i] = res[i - 1]->ai_next; else res[i] = res[1]; } } else { if (res[1]) { freeaddrinfo(res[1]); res[1] = NULL; } multilink = 0; } } else multilink = 0; } if ((stream_idx > 0) && skip_data) { if (clientserver && !client && (stream_idx == 1)) { /* send server "OK" message */ fprintf(stdout, "OK v%d.%d.%d\n", vers_major, vers_minor, vers_delta); fflush(stdout); } break; } bzero((char *)&sinme[stream_idx], sizeof(sinme[stream_idx])); bzero((char *)&sinhim[stream_idx], sizeof(sinhim[stream_idx])); #ifdef AF_INET6 bzero((char *)&sinme6[stream_idx], sizeof(sinme6[stream_idx])); bzero((char *)&sinhim6[stream_idx], sizeof(sinhim6[stream_idx])); #endif if (((trans && !reverse) && (stream_idx > 0)) || ((!trans && reverse) && (stream_idx > 0)) || (client && (stream_idx == 0))) { /* xmitr initiates connections (unless reversed) */ if (client) { if (af == AF_INET) { sinhim[stream_idx].sin_family = af; if (ipad_stride.ip32 && (stream_idx > 1)) { sinhim[stream_idx].sin_addr.s_addr = sinhim[stream_idx - 1].sin_addr.s_addr + ipad_stride.ip32; } else { bcopy((char *)&(((struct sockaddr_in *)res[stream_idx]->ai_addr)->sin_addr), (char *)&sinhim[stream_idx].sin_addr.s_addr, sizeof(sinhim[stream_idx].sin_addr.s_addr)); } } #ifdef AF_INET6 else if (af == AF_INET6) { sinhim6[stream_idx].sin6_family = af; bcopy((char *)&(((struct sockaddr_in6 *)res[stream_idx]->ai_addr)->sin6_addr), (char *)&sinhim6[stream_idx].sin6_addr.s6_addr, sizeof(sinhim6[stream_idx].sin6_addr.s6_addr)); sinhim6[stream_idx].sin6_scope_id = ((struct sockaddr_in6 *)res[stream_idx]->ai_addr)->sin6_scope_id; } #endif else { err("unsupported AF"); } } else { sinhim[stream_idx].sin_family = af; if (ipad_stride.ip32 && (stream_idx > 1)) { sinhim[stream_idx].sin_addr.s_addr = sinhim[stream_idx - 1].sin_addr.s_addr + ipad_stride.ip32; } else { if (multilink && (stream_idx > 0)) sinhim[stream_idx].sin_addr = ((struct sockaddr_in *)res[stream_idx]->ai_addr)->sin_addr; else sinhim[stream_idx].sin_addr = clientaddr; } #ifdef AF_INET6 sinhim6[stream_idx].sin6_family = af; if (multilink && (stream_idx > 0)) sinhim6[stream_idx].sin6_addr = ((struct sockaddr_in6 *)res[stream_idx]->ai_addr)->sin6_addr; else sinhim6[stream_idx].sin6_addr = clientaddr6; sinhim6[stream_idx].sin6_scope_id = clientscope6; #endif } if (stream_idx == 0) { sinhim[stream_idx].sin_port = htons(ctlport); sinme[stream_idx].sin_port = htons(srcctlport); /* default is free choice */ #ifdef AF_INET6 sinhim6[stream_idx].sin6_port = htons(ctlport); sinme6[stream_idx].sin6_port = htons(srcctlport); /* default is free choice */ #endif } else { sinhim[stream_idx].sin_port = htons(port + stream_idx - 1); sinme[stream_idx].sin_port = htons(srcport); /* default is free choice */ #ifdef AF_INET6 sinhim6[stream_idx].sin6_port = htons(port + stream_idx - 1); sinme6[stream_idx].sin6_port = htons(srcport); /* default is free choice */ #endif } } else { /* rcvr listens for connections (unless reversed) */ if (stream_idx == 0) { sinme[stream_idx].sin_port = htons(ctlport); sinhim[stream_idx].sin_port = htons(srcctlport); /* default is free choice */ #ifdef AF_INET6 sinme6[stream_idx].sin6_port = htons(ctlport); sinhim6[stream_idx].sin6_port = htons(srcctlport); /* default is free choice */ #endif } else { sinme[stream_idx].sin_port = htons(port + stream_idx - 1); sinhim[stream_idx].sin_port = htons(srcport); /* default is free choice */ #ifdef AF_INET6 sinme6[stream_idx].sin6_port = htons(port + stream_idx - 1); sinhim6[stream_idx].sin6_port = htons(srcport); /* default is free choice */ #endif } } sinme[stream_idx].sin_family = af; #ifdef AF_INET6 sinme6[stream_idx].sin6_family = af; #endif if ((fd[stream_idx] = socket(domain, (udp && (stream_idx != 0))?SOCK_DGRAM:SOCK_STREAM, 0)) < 0) { if (clientserver && !client && (stream_idx == 1)) { save_errno = errno; fputs("KO\n", stdout); mes("Error: socket() on data stream failed"); fputs("Error: ", stdout); fputs(strerror(save_errno), stdout); fputs("\n", stdout); fputs("KO\n", stdout); goto cleanup; } err("socket"); } if (setsockopt(fd[stream_idx], SOL_SOCKET, SO_REUSEADDR, (void *)&one, sizeof(one)) < 0) { if (clientserver && !client && (stream_idx == 1)) { save_errno = errno; fputs("KO\n", stdout); mes("Error: setsockopt()" " to so_reuseaddr failed"); fputs("Error: ", stdout); fputs(strerror(save_errno), stdout); fputs("\n", stdout); fputs("KO\n", stdout); goto cleanup; } err("setsockopt: so_reuseaddr"); } #ifdef IPV6_V6ONLY if ((af == AF_INET6) && explicitaf && !v4mapped) { if (setsockopt(fd[stream_idx], IPPROTO_IPV6, IPV6_V6ONLY, (void *)&one, sizeof(int)) < 0) { if (clientserver && !client && (stream_idx == 1)) { save_errno = errno; fputs("KO\n", stdout); mes("Error: setsockopt()" " to ipv6_only failed"); fputs("Error: ", stdout); fputs(strerror(save_errno), stdout); fputs("\n", stdout); fputs("KO\n", stdout); goto cleanup; } err("setsockopt: ipv6_only"); } } #endif if (af == AF_INET) { if (bind(fd[stream_idx], (struct sockaddr *)&sinme[stream_idx], sizeof(sinme[stream_idx])) < 0) { if (clientserver && !client && (stream_idx == 1)) { save_errno = errno; fputs("KO\n", stdout); mes("Error: bind() on data stream failed"); fputs("Error: ", stdout); fputs(strerror(save_errno), stdout); fputs("\n", stdout); if (((!trans && !reverse) || (trans && reverse)) && (errno == EADDRINUSE) && (port == IPERF_PORT)) fputs("Info: Possible collision" " with iperf server\n", stdout); fputs("KO\n", stdout); goto cleanup; } if (clientserver && client && (stream_idx == 1) && ((!trans && !reverse) || (trans && reverse)) && (errno == EADDRINUSE) && (port == IPERF_PORT)) { errmes("bind"); fputs("Info: Possible collision" " with iperf server\n", stderr); fflush(stderr); exit(1); } err("bind"); } } #ifdef AF_INET6 else if (af == AF_INET6) { if (bind(fd[stream_idx], (struct sockaddr *)&sinme6[stream_idx], sizeof(sinme6[stream_idx])) < 0) { if (clientserver && !client && (stream_idx == 1)) { save_errno = errno; fputs("KO\n", stdout); mes("Error: bind() on data stream failed"); fputs("Error: ", stdout); fputs(strerror(save_errno), stdout); fputs("\n", stdout); fputs("KO\n", stdout); goto cleanup; } err("bind"); } } #endif else { if (clientserver && !client && (stream_idx == 1)) { save_errno = errno; fputs("KO\n", stdout); mes("Error: unsupported AF on data stream"); fputs("Error: ", stdout); fputs(strerror(save_errno), stdout); fputs("\n", stdout); fputs("KO\n", stdout); goto cleanup; } err("unsupported AF"); } if (clientserver && !client && (stream_idx == 1)) { /* finally OK to send server "OK" message */ fprintf(stdout, "OK v%d.%d.%d\n", vers_major, vers_minor, vers_delta); fflush(stdout); if ((trans && !reverse) || (!trans && reverse)) usleep(50000); } if (clientserver && (stream_idx == 1)) { if (!udp && trans) { nretrans[0] = get_retrans(fd[0], &tcpinf); if ((retransinfo > 0) || cwndinfo) b_flag = 1; } } if (stream_idx == nstream) { if (brief <= 0) mes("socket"); #ifdef HAVE_SETPRIO if (priority && (brief <= 0)) { errno = 0; priority = getpriority(PRIO_PROCESS, 0); if (errno) mes("couldn't get priority"); else { if (format & PARSE) fprintf(stdout, "nuttcp%s%s: " "priority=%d\n", trans ? "-t" : "-r", ident, priority); else fprintf(stdout, "nuttcp%s%s: " "priority = %d\n", trans ? "-t" : "-r", ident, priority); } } #endif #ifdef HAVE_SETAFFINITY if ((affinity >= 0) && (brief <= 0) && !host3) { int cpu_affinity; errno = 0; sched_getaffinity(0, sizeof(cpu_set_t), &cpu_set); if (errno) mes("couldn't get affinity"); else { for ( cpu_affinity = 0; cpu_affinity < ncores; cpu_affinity++ ) { if (CPU_ISSET(cpu_affinity, &cpu_set)) break; } if (format & PARSE) fprintf(stdout, "nuttcp%s%s: " "cpu_affinity=%d\n", trans ? "-t" : "-r", ident, cpu_affinity); else fprintf(stdout, "nuttcp%s%s: " "affinity = CPU %d\n", trans ? "-t" : "-r", ident, cpu_affinity); } } #endif if (trans) { if ((brief <= 0) && (format & PARSE)) { fprintf(stdout, "nuttcp-t%s: buflen=%d ", ident, buflen); if (nbuf != INT_MAX) fprintf(stdout, "nbuf=%llu ", nbuf); fprintf(stdout, "nstream=%d port=%d", nstream, port); if (srcport) fprintf(stdout, " srcport=%d", srcport); fprintf(stdout, " mode=%s host=%s", udp?"udp":"tcp", host); if (multicast) fprintf(stdout, " multicast_ttl=%d", multicast); fprintf(stdout, "\n"); if (timeout) fprintf(stdout, "nuttcp-t%s: time_limit=%.2f\n", ident, timeout); if ((rate != MAXRATE) || tos) fprintf(stdout, "nuttcp-t%s:", ident); if (rate != MAXRATE) { fprintf(stdout, " rate_limit=%.3f rate_unit=Mbps rate_mode=%s", (double)rate/1000, irate ? "instantaneous" : "aggregate"); if (iratesss) fprintf(stdout, " smoothed_slow_start=on"); if (maxburst > 1) fprintf(stdout, " packet_burst=%d", maxburst); if (udp) { unsigned long long ppsrate = ((uint64_t)rate * 1000)/8/buflen; fprintf(stdout, " pps_rate=%llu", ppsrate); } } if (tos) fprintf(stdout, " tos=0x%X", tos); if ((rate != MAXRATE) || tos) fprintf(stdout, "\n"); } else if (brief <= 0) { fprintf(stdout, "nuttcp-t%s: buflen=%d, ", ident, buflen); if (nbuf != INT_MAX) fprintf(stdout, "nbuf=%llu, ", nbuf); fprintf(stdout, "nstream=%d, port=%d", nstream, port); if (srcport) fprintf(stdout, ", srcport=%d", srcport); fprintf(stdout, " %s -> %s", udp?"udp":"tcp", host); if (multicast) fprintf(stdout, " ttl=%d", multicast); fprintf(stdout, "\n"); if (timeout) fprintf(stdout, "nuttcp-t%s: time limit = %.2f second%s\n", ident, timeout, (timeout == 1.0)?"":"s"); if ((rate != MAXRATE) || tos) fprintf(stdout, "nuttcp-t%s:", ident); if (rate != MAXRATE) { fprintf(stdout, " rate limit = %.3f Mbps (%s)", (double)rate/1000, irate ? "instantaneous" : "aggregate"); if (iratesss) fprintf(stdout, " with smoothed slow start"); if (maxburst > 1) fprintf(stdout, ", packet burst = %d", maxburst); if (udp) { unsigned long long ppsrate = ((uint64_t)rate * 1000)/8/buflen; fprintf(stdout, ", %llu pps", ppsrate); } if (tos) fprintf(stdout, ","); } if (tos) fprintf(stdout, " tos = 0x%X", tos); if ((rate != MAXRATE) || tos) fprintf(stdout, "\n"); } } else { if ((brief <= 0) && (format & PARSE)) { fprintf(stdout, "nuttcp-r%s: buflen=%d ", ident, buflen); if (nbuf != INT_MAX) fprintf(stdout, "nbuf=%llu ", nbuf); fprintf(stdout, "nstream=%d port=%d", nstream, port); if (srcport) fprintf(stdout, " srcport=%d", srcport); fprintf(stdout, " mode=%s\n", udp ? "udp":"tcp"); if (tos) fprintf(stdout, "nuttcp-r%s: tos=0x%X\n", ident, tos); if (interval) fprintf(stdout, "nuttcp-r%s: reporting_interval=%.2f\n", ident, interval); } else if (brief <= 0) { fprintf(stdout, "nuttcp-r%s: buflen=%d, ", ident, buflen); if (nbuf != INT_MAX) fprintf(stdout, "nbuf=%llu, ", nbuf); fprintf(stdout, "nstream=%d, port=%d", nstream, port); if (srcport) fprintf(stdout, ", srcport=%d", srcport); fprintf(stdout, " %s\n", udp ? "udp":"tcp"); if (tos) fprintf(stdout, "nuttcp-r%s: tos = 0x%X\n", ident, tos); if (interval) fprintf(stdout, "nuttcp-r%s: interval reporting every %.2f second%s\n", ident, interval, (interval == 1.0)?"":"s"); } } } if (stream_idx > 0) { if (trans) { /* Set the transmitter options */ if (sendwin) { if (setsockopt(fd[stream_idx], SOL_SOCKET, SO_SNDBUF, (void *)&sendwin, sizeof(sendwin)) < 0) errmes("unable to setsockopt SO_SNDBUF"); if (braindead && (setsockopt(fd[stream_idx], SOL_SOCKET, SO_RCVBUF, (void *)&rcvwin, sizeof(rcvwin)) < 0)) errmes("unable to setsockopt SO_RCVBUF"); } if (tos) { if (af == AF_INET) { if (setsockopt(fd[stream_idx], IPPROTO_IP, IP_TOS, (void *)&tos, sizeof(tos)) < 0) err("setsockopt"); } #ifdef AF_INET6 else if (af == AF_INET6) { if (setsockopt(fd[stream_idx], IPPROTO_IPV6, IPV6_TCLASS, (void *)&tos, sizeof(tos)) < 0) err("setsockopt"); } #endif else { err("unsupported AF"); } } if (nodelay && !udp) { struct protoent *p; p = getprotobyname("tcp"); if (p && setsockopt(fd[stream_idx], p->p_proto, TCP_NODELAY, (void *)&one, sizeof(one)) < 0) err("setsockopt: nodelay"); if ((stream_idx == nstream) && (brief <= 0)) mes("nodelay"); } } else { /* Set the receiver options */ if (rcvwin) { if (setsockopt(fd[stream_idx], SOL_SOCKET, SO_RCVBUF, (void *)&rcvwin, sizeof(rcvwin)) < 0) errmes("unable to setsockopt SO_RCVBUF"); if (braindead && (setsockopt(fd[stream_idx], SOL_SOCKET, SO_SNDBUF, (void *)&sendwin, sizeof(sendwin)) < 0)) errmes("unable to setsockopt SO_SNDBUF"); } if (tos) { if (af == AF_INET) { if (setsockopt(fd[stream_idx], IPPROTO_IP, IP_TOS, (void *)&tos, sizeof(tos)) < 0) err("setsockopt"); } #ifdef AF_INET6 else if (af == AF_INET6) { if (setsockopt(fd[stream_idx], IPPROTO_IPV6, IPV6_TCLASS, (void *)&tos, sizeof(tos)) < 0) err("setsockopt"); } #endif else { err("unsupported AF"); } } } } if (!udp || (stream_idx == 0)) { if (((trans && !reverse) && (stream_idx > 0)) || ((!trans && reverse) && (stream_idx > 0)) || (client && (stream_idx == 0))) { /* The transmitter initiates the connection * (unless reversed by the flip option) */ if (options && (stream_idx > 0)) { if (setsockopt(fd[stream_idx], SOL_SOCKET, options, (void *)&one, sizeof(one)) < 0) errmes("unable to setsockopt options"); } usleep(20000); if (trans && (stream_idx > 0) && datamss) { #if defined(__CYGWIN__) || defined(_WIN32) if (format & PARSE) fprintf(stderr, "nuttcp%s%s: Warning=\"setting_maximum_segment_size_not_supported_on_windows\"\n", trans?"-t":"-r", ident); else fprintf(stderr, "nuttcp%s%s: Warning: setting maximum segment size not supported on windows\n", trans?"-t":"-r", ident); fflush(stderr); #endif optlen = sizeof(datamss); if ((sockopterr = setsockopt(fd[stream_idx], IPPROTO_TCP, TCP_MAXSEG, (void *)&datamss, optlen)) < 0) if (errno != EINVAL) err("unable to set maximum segment size"); } if (clientserver && !client && (stream_idx == 1)) { /* check if client went away */ pollfds[0].fd = fileno(ctlconn); save_events = pollfds[0].events; pollfds[0].events = POLLIN | POLLPRI; pollfds[0].revents = 0; if ((poll(pollfds, 1, 0) > 0) && (pollfds[0].revents & (POLLIN | POLLPRI))) goto cleanup; pollfds[0].events = save_events; } num_connect_tries++; if (stream_idx == 1) get_timeofday(&timeconn1, (struct timezone *)0); if (af == AF_INET) { error_num = connect(fd[stream_idx], (struct sockaddr *)&sinhim[stream_idx], sizeof(sinhim[stream_idx])); } #ifdef AF_INET6 else if (af == AF_INET6) { error_num = connect(fd[stream_idx], (struct sockaddr *)&sinhim6[stream_idx], sizeof(sinhim6[stream_idx])); } #endif else { err("unsupported AF"); } if (error_num < 0) { if (clientserver && client && (stream_idx == 0) && ((errno == ECONNREFUSED) || (errno == ECONNRESET)) && (num_connect_tries < MAX_CONNECT_TRIES) && retry_server) { /* retry control connection to * server for certain possibly * transient errors */ usleep(SERVER_RETRY_USEC); goto doit; } if (!trans && (stream_idx == 0)) err("connect"); if (stream_idx > 0) { if (clientserver && !client) { for ( i = 1; i <= stream_idx; i++ ) close(fd[i]); goto cleanup; } err("connect"); } if (stream_idx == 0) { clientserver = 0; if (thirdparty) { perror("3rd party connect failed"); fprintf(stderr, "3rd party nuttcp only supported for client/server mode\n"); fflush(stderr); exit(1); } if (interval) { perror("connect failed"); fprintf(stderr, "interval option only supported for client/server mode\n"); fflush(stderr); exit(1); } if (reverse) { perror("connect failed"); fprintf(stderr, "flip option only supported for client/server mode\n"); fflush(stderr); exit(1); } if (traceroute) { perror("connect failed"); fprintf(stderr, "traceroute option only supported for client/server mode\n"); fflush(stderr); exit(1); } if (host3) { perror("connect failed"); fprintf(stderr, "3rd party nuttcp only supported for client/server mode\n"); fflush(stderr); exit(1); } if (multicast) { perror("connect failed"); fprintf(stderr, "multicast only supported for client/server mode\n"); fflush(stderr); exit(1); } if (udp) { perror("connect failed"); fprintf(stderr, "UDP transfers only supported for client/server mode\n"); fflush(stderr); exit(1); } if (format & PARSE) { fprintf(stderr, "nuttcp%s%s: Info=\"attempting_to_switch_to_deprecated_classic_mode\"\n", trans?"-t":"-r", ident); fprintf(stderr, "nuttcp%s%s: Info=\"will_use_less_reliable_transmitter_side_statistics\"\n", trans?"-t":"-r", ident); } else { fprintf(stderr, "nuttcp%s%s: Info: attempting to switch to deprecated \"classic\" mode\n", trans?"-t":"-r", ident); fprintf(stderr, "nuttcp%s%s: Info: will use less reliable transmitter side statistics\n", trans?"-t":"-r", ident); } fflush(stderr); } } if (stream_idx == 1) { get_timeofday(&timeconn2, (struct timezone *)0); tvsub( &timeconn, &timeconn2, &timeconn1 ); rtt = timeconn.tv_sec*1000 + ((double)timeconn.tv_usec)/1000; } if (sockopterr && trans && (stream_idx > 0) && datamss) { optlen = sizeof(datamss); if ((sockopterr = setsockopt(fd[stream_idx], IPPROTO_TCP, TCP_MAXSEG, (void *)&datamss, optlen)) < 0) { if (errno != EINVAL) err("unable to set maximum segment size"); else err("setting maximum segment size not supported on this OS"); } } if (stream_idx == nstream) { optlen = sizeof(datamss); if (getsockopt(fd[stream_idx], IPPROTO_TCP, TCP_MAXSEG, (void *)&datamss, &optlen) < 0) err("get dataconn maximum segment size didn't work"); if (format & DEBUGMTU) fprintf(stderr, "datamss = %d\n", datamss); } if ((stream_idx == nstream) && (brief <= 0)) { char tmphost[ADDRSTRLEN] = "\0"; if (af == AF_INET) { inet_ntop(af, &sinhim[stream_idx].sin_addr.s_addr, tmphost, sizeof(tmphost)); } #ifdef AF_INET6 else if (af == AF_INET6) { inet_ntop(af, sinhim6[stream_idx].sin6_addr.s6_addr, tmphost, sizeof(tmphost)); } #endif else { err("unsupported AF"); } if (format & PARSE) { fprintf(stdout, "nuttcp%s%s: connect=%s", trans?"-t":"-r", ident, tmphost); if (trans && datamss) { fprintf(stdout, " mss=%d", datamss); } if (rtt) fprintf(stdout, P_RTT_FMT, rtt); } else { fprintf(stdout, "nuttcp%s%s: connect to %s", trans?"-t":"-r", ident, tmphost); if (rtt || (trans && datamss)) fprintf(stdout, " with"); if (trans && datamss) { fprintf(stdout, " mss=%d", datamss); if (rtt) fprintf(stdout, ","); } if (rtt) fprintf(stdout, RTT_FMT, rtt); } fprintf(stdout, "\n"); } } else { /* The receiver listens for the connection * (unless reversed by the flip option) */ if (trans && (stream_idx > 0) && datamss) { #if defined(__CYGWIN__) || defined(_WIN32) if (format & PARSE) fprintf(stderr, "nuttcp%s%s: Warning=\"setting_maximum_segment_size_not_supported_on_windows\"\n", trans?"-t":"-r", ident); else fprintf(stderr, "nuttcp%s%s: Warning: setting maximum segment size not supported on windows\n", trans?"-t":"-r", ident); fflush(stderr); #endif optlen = sizeof(datamss); if ((sockopterr = setsockopt(fd[stream_idx], IPPROTO_TCP, TCP_MAXSEG, (void *)&datamss, optlen)) < 0) if (errno != EINVAL) err("unable to set maximum segment size"); } listen(fd[stream_idx], LISTEN_BACKLOG); if (clientserver && !client && (stream_idx == 0) && !inetd && !nofork && !forked) { if ((pid = fork()) == (pid_t)-1) err("can't fork"); if (pid != 0) exit(0); forked = 1; if (sinkmode) { close(0); close(1); close(2); open("/dev/null", O_RDWR); dup(0); dup(0); } setsid(); } if (options && (stream_idx > 0)) { if (setsockopt(fd[stream_idx], SOL_SOCKET, options, (void *)&one, sizeof(one)) < 0) errmes("unable to setsockopt options"); } if (sockopterr && trans && (stream_idx > 0) && datamss) { optlen = sizeof(datamss); if ((sockopterr = setsockopt(fd[stream_idx], IPPROTO_TCP, TCP_MAXSEG, (void *)&datamss, optlen)) < 0) if (errno != EINVAL) err("unable to set maximum segment size"); } if (clientserver && (stream_idx > 0)) { sigact.sa_handler = ignore_alarm; sigemptyset(&sigact.sa_mask); sigact.sa_flags = 0; sigaction(SIGALRM, &sigact, &savesigact); alarm(ACCEPT_TIMEOUT); } acceptnewconn: fromlen = sizeof(frominet); nfd=accept(fd[stream_idx], (struct sockaddr *)&frominet, &fromlen); save_errno = errno; if (clientserver && (stream_idx > 0)) { alarm(0); sigact.sa_handler = savesigact.sa_handler; sigact.sa_mask = savesigact.sa_mask; sigact.sa_flags = savesigact.sa_flags; sigaction(SIGALRM, &sigact, 0); } if (nfd < 0) { /* check for interrupted system call - on * server, close data streams, cleanup and * try again - all other errors just die */ if ((save_errno == EINTR) && clientserver && (stream_idx > 0)) { if (client) { /* if client, just give nice * error message and exit */ mes("Error: accept() timeout"); exit(1); } for ( i = 1; i <= stream_idx; i++ ) close(fd[i]); goto cleanup; } err("accept"); } if (clientserver && !client && (stream_idx == 0) && !inetd && !nofork && !single_threaded) { /* multi-threaded manually started server */ if ((pid = fork()) == (pid_t)-1) err("can't fork"); if (pid != 0) { /* parent just waits for quick * child exit */ while ((wait_pid = wait(&pidstat)) != pid) { if (wait_pid == (pid_t)-1) { if (errno == ECHILD) break; err("wait failed"); } } /* and then accept()s another client * connection */ close(nfd); stream_idx = 0; if (oneshot) exit(0); goto acceptnewconn; } /* child just makes a grandchild and then * immediately exits (avoid zombie processes) */ if ((pid = fork()) == (pid_t)-1) err("can't fork"); if (pid != 0) exit(0); /* grandkid does all the work */ oneshot = 1; } af = frominet.ss_family; close(fd[stream_idx]); fd[stream_idx]=nfd; if (sockopterr && trans && (stream_idx > 0) && datamss) { optlen = sizeof(datamss); if ((sockopterr = setsockopt(fd[stream_idx], IPPROTO_TCP, TCP_MAXSEG, (void *)&datamss, optlen)) < 0) { if (errno != EINVAL) err("unable to set maximum segment size"); else err("setting maximum segment size not supported on this OS"); } } if (stream_idx == nstream) { optlen = sizeof(datamss); if (getsockopt(fd[stream_idx], IPPROTO_TCP, TCP_MAXSEG, (void *)&datamss, &optlen) < 0) err("get dataconn maximum segment size didn't work"); if (format & DEBUGMTU) fprintf(stderr, "datamss = %d\n", datamss); } if (af == AF_INET) { struct sockaddr_in peer; socklen_t peerlen = sizeof(peer); if (getpeername(fd[stream_idx], (struct sockaddr *)&peer, &peerlen) < 0) { err("getpeername"); } if ((stream_idx == nstream) && (brief <= 0)) { char tmphost[ADDRSTRLEN] = "\0"; inet_ntop(af, &peer.sin_addr.s_addr, tmphost, sizeof(tmphost)); if (format & PARSE) { fprintf(stdout, "nuttcp%s%s: accept=%s", trans?"-t":"-r", ident, tmphost); if (trans && datamss) { fprintf(stdout, " mss=%d", datamss); } } else { fprintf(stdout, "nuttcp%s%s: accept from %s", trans?"-t":"-r", ident, tmphost); if (trans && datamss) { fprintf(stdout, " with mss=%d", datamss); } } fprintf(stdout, "\n"); } if (stream_idx == 0) { clientaddr = peer.sin_addr; client_ipaddr.ss.ss_family = AF_INET; client_ipaddr.sin.sin_addr = clientaddr; } } #ifdef AF_INET6 else if (af == AF_INET6) { struct sockaddr_in6 peer; socklen_t peerlen = sizeof(peer); if (getpeername(fd[stream_idx], (struct sockaddr *)&peer, &peerlen) < 0) { err("getpeername"); } if ((stream_idx == nstream) && (brief <= 0)) { char tmphost[ADDRSTRLEN] = "\0"; inet_ntop(af, peer.sin6_addr.s6_addr, tmphost, sizeof(tmphost)); if (format & PARSE) { fprintf(stdout, "nuttcp%s%s: accept=%s", trans?"-t":"-r", ident, tmphost); if (trans && datamss) { fprintf(stdout, " mss=%d", datamss); } } else { fprintf(stdout, "nuttcp%s%s: accept from %s", trans?"-t":"-r", ident, tmphost); if (trans && datamss) { fprintf(stdout, " with mss=%d", datamss); } } fprintf(stdout, "\n"); } if (stream_idx == 0) { clientaddr6 = peer.sin6_addr; clientscope6 = peer.sin6_scope_id; client_ipaddr.ss.ss_family = AF_INET6; client_ipaddr.sin6.sin6_addr = clientaddr6; } } #endif else { err("unsupported AF"); } } } if (!udp && trans && (stream_idx >= 1) && (retransinfo > 0)) { if ((stream_idx == 1) || (retransinfo == 1)) { nretrans[stream_idx] = get_retrans(fd[stream_idx], &tcpinf); iretrans[stream_idx] = nretrans[stream_idx]; #if defined(linux) if (retransinfo == 1) { cwnd[stream_idx] = tcpinf.tcpinfo_snd_cwnd *datamss/1024; if (stream_idx == 1) { init_pkt_cwnd = tcpinf.tcpinfo_snd_cwnd; sss_pkt_cwnd = init_pkt_cwnd; } } #endif } } optlen = sizeof(sendwinval); if (getsockopt(fd[stream_idx], SOL_SOCKET, SO_SNDBUF, (void *)&sendwinval, &optlen) < 0) err("get send window size didn't work"); #if defined(linux) sendwinval /= 2; #endif if ((stream_idx > 0) && sendwin && (trans || braindead) && (sendwinval < (0.98 * sendwin))) { if (format & PARSE) fprintf(stderr, "nuttcp%s%s: Warning=\"send_window_size_%d_<_requested_window_size_%d\"\n", trans?"-t":"-r", ident, sendwinval, sendwin); else fprintf(stderr, "nuttcp%s%s: Warning: send window size %d < requested window size %d\n", trans?"-t":"-r", ident, sendwinval, sendwin); fflush(stderr); } optlen = sizeof(rcvwinval); if (getsockopt(fd[stream_idx], SOL_SOCKET, SO_RCVBUF, (void *)&rcvwinval, &optlen) < 0) err("Get recv window size didn't work"); #if defined(linux) rcvwinval /= 2; #endif if ((stream_idx > 0) && rcvwin && (!trans || braindead) && (rcvwinval < (0.98 * rcvwin))) { if (format & PARSE) fprintf(stderr, "nuttcp%s%s: Warning=\"receive_window_size_%d_<_requested_window_size_%d\"\n", trans?"-t":"-r", ident, rcvwinval, rcvwin); else fprintf(stderr, "nuttcp%s%s: Warning: receive window size %d < requested window size %d\n", trans?"-t":"-r", ident, rcvwinval, rcvwin); fflush(stderr); } if (firsttime) { firsttime = 0; origsendwin = sendwinval; origrcvwin = rcvwinval; } if ((stream_idx == nstream) && (brief <= 0)) { #if defined(linux) FILE *adv_ws; sendwinval *= 2; rcvwinval *= 2; if ((adv_ws = fopen(TCP_ADV_WIN_SCALE, "r"))) { if (fscanf(adv_ws, "%d", &winadjust) <= 0) winadjust = 2; fclose(adv_ws); } else { winadjust = 2; } if (winadjust < 0) { sendwinavail = sendwinval >> -winadjust; rcvwinavail = rcvwinval >> -winadjust; } else if (winadjust > 0) { sendwinavail = sendwinval - (sendwinval >> winadjust); rcvwinavail = rcvwinval - (rcvwinval >> winadjust); } #endif if (format & PARSE) fprintf(stdout, "nuttcp%s%s: send_window_size=%d receive_window_size=%d\n", trans?"-t":"-r", ident, sendwinval, rcvwinval); else fprintf(stdout, "nuttcp%s%s: send window size = %d, receive window size = %d\n", trans?"-t":"-r", ident, sendwinval, rcvwinval); #if defined(linux) if (format & PARSE) fprintf(stdout, "nuttcp%s%s: send_window_avail=%d receive_window_avail=%d\n", trans?"-t":"-r", ident, sendwinavail, rcvwinavail); else fprintf(stdout, "nuttcp%s%s: available send window = %d, available receive window = %d\n", trans?"-t":"-r", ident, sendwinavail, rcvwinavail); if (!udp && trans && init_pkt_cwnd) { if (format & PARSE) fprintf(stdout, "nuttcp%s%s: initial_congestion_window_kb=%d initial_congestion_window_pkt=%d\n", trans?"-t":"-r", ident, init_pkt_cwnd*datamss/1024, init_pkt_cwnd); else fprintf(stdout, "nuttcp%s%s: initial congestion window = %d KB (%d packets)\n", trans?"-t":"-r", ident, init_pkt_cwnd*datamss/1024, init_pkt_cwnd); } #endif } } if (abortconn) exit(1); if (host3 && clientserver) { char path[64]; char *cmd; fflush(stdout); fflush(stderr); cmd = nut_cmd; if (client) { if ((pid = fork()) == (pid_t)-1) err("can't fork"); if (pid == 0) { if (interval) { itimer.it_value.tv_sec = interval; } else if (timeout) { itimer.it_value.tv_sec = timeout; } else { if (rate != MAXRATE) itimer.it_value.tv_sec = (double)(2*nbuf*buflen) /rate/125; else itimer.it_value.tv_sec = (double)(nbuf*buflen) /LOW_RATE_HOST3/125; if (itimer.it_value.tv_sec < 7200) itimer.it_value.tv_sec = 7200; } itimer.it_value.tv_sec += idle_data_max < SRVR_INFO_TIMEOUT ? SRVR_INFO_TIMEOUT : idle_data_max; itimer.it_value.tv_usec = 0; itimer.it_interval.tv_sec = 0; itimer.it_interval.tv_usec = 0; setitimer(ITIMER_REAL, &itimer, 0); while (fgets(linebuf, sizeof(linebuf), stdin) && !intr) { setitimer(ITIMER_REAL, &itimer, 0); if (strncmp(linebuf, "DONE", 4) == 0) exit(0); if (*ident && (*linebuf != '\n')) fprintf(stdout, "%s: ", ident + 1); fputs(linebuf, stdout); fflush(stdout); } itimer.it_value.tv_sec = 0; itimer.it_value.tv_usec = 0; setitimer(ITIMER_REAL, &itimer, 0); exit(0); } signal(SIGINT, SIG_IGN); while ((wait_pid = wait(&pidstat)) != pid) { if (wait_pid == (pid_t)-1) { if (errno == ECHILD) break; err("wait failed"); } } exit(0); } else { if ((pid = fork()) == (pid_t)-1) err("can't fork"); if (pid != 0) { sigact.sa_handler = &sigalarm; sigemptyset(&sigact.sa_mask); sigact.sa_flags = 0; sigaction(SIGALRM, &sigact, 0); alarm(10); while ((wait_pid = wait(&pidstat)) != pid) { if (wait_pid == (pid_t)-1) { if (errno == ECHILD) break; if (errno == EINTR) { pollfds[0].fd = fileno(ctlconn); pollfds[0].events = POLLIN | POLLPRI; pollfds[0].revents = 0; if ((poll(pollfds, 1, 0) > 0) && (pollfds[0].revents & (POLLIN | POLLPRI))) { kill(pid, SIGINT); sleep(1); kill(pid, SIGINT); continue; } sigact.sa_handler = &sigalarm; sigemptyset(&sigact.sa_mask); sigact.sa_flags = 0; sigaction(SIGALRM, &sigact, 0); alarm(10); continue; } err("wait failed"); } } fprintf(stdout, "DONE\n"); fflush(stdout); goto cleanup; } close(2); dup(1); i = 0; j = 0; cmdargs[i++] = cmd; cmdargs[i++] = "-3"; if (ctlport3) { sprintf(tmpargs[j], "-P%hu", ctlport3); cmdargs[i++] = tmpargs[j++]; } else { if (pass_ctlport) { sprintf(tmpargs[j], "-P%hu", ctlport); cmdargs[i++] = tmpargs[j++]; } } if (affinity >= 0) { sprintf(tmpargs[j], "-xc%d", affinity); cmdargs[i++] = tmpargs[j++]; } if (srvr_affinity >= 0) { sprintf(tmpargs[j], "-xcs%d", srvr_affinity); cmdargs[i++] = tmpargs[j++]; } if (irvers < 50302) { if ((udp && !multicast && (buflen != DEFAULTUDPBUFLEN)) || (udp && multicast && (buflen != DEFAULT_MC_UDPBUFLEN)) || (!udp && (buflen != 65536))) { sprintf(tmpargs[j], "-l%d", buflen); cmdargs[i++] = tmpargs[j++]; } } else if (buflen) { sprintf(tmpargs[j], "-l%d", buflen); cmdargs[i++] = tmpargs[j++]; } if (nbuf != INT_MAX) { if (nbuf_bytes) sprintf(tmpargs[j], "-n%llub", nbuf); else sprintf(tmpargs[j], "-n%llu", nbuf); cmdargs[i++] = tmpargs[j++]; } if (brief3 != 1) { sprintf(tmpargs[j], "-b%d", brief3); cmdargs[i++] = tmpargs[j++]; } if (sendwin) { sprintf(tmpargs[j], "-w%d", sendwin/1024); cmdargs[i++] = tmpargs[j++]; } if (nstream != 1) { sprintf(tmpargs[j], "-N%d%s", nstream, multilink ? "m" : ""); cmdargs[i++] = tmpargs[j++]; } if (rate != MAXRATE) { if (maxburst > 1) { if (rate_pps) sprintf(tmpargs[j], "-R%s%lup/%d", irate ? "i" : "", rate, maxburst); else sprintf(tmpargs[j], "-R%s%lu/%d", irate ? "i" : "", rate, maxburst); } else { if (rate_pps) sprintf(tmpargs[j], "-R%s%s%lup", irate ? "i" : "", iratesss ? "s" : "", rate); else sprintf(tmpargs[j], "-R%s%s%lu", irate ? "i" : "", iratesss ? "s" : "", rate); } cmdargs[i++] = tmpargs[j++]; } else { if (udp && !multicast) cmdargs[i++] = "-R0"; } if (srcport) { sprintf(tmpargs[j], "-p%hu:%hu", srcport, port); cmdargs[i++] = tmpargs[j++]; } else if (port != DEFAULT_PORT) { sprintf(tmpargs[j], "-p%hu", port); cmdargs[i++] = tmpargs[j++]; } if (trans) cmdargs[i++] = "-r"; if (braindead) cmdargs[i++] = "-wb"; if (timeout && (timeout != DEFAULT_TIMEOUT)) { sprintf(tmpargs[j], "-T%f", timeout); cmdargs[i++] = tmpargs[j++]; } if (udp) { if (multicast) { if (ssm == 1) sprintf(tmpargs[j], "-ms%d", multicast); else if (ssm == 0) sprintf(tmpargs[j], "-ma%d", multicast); else sprintf(tmpargs[j], "-m%d", multicast); cmdargs[i++] = tmpargs[j++]; if (mc_addr) { sprintf(tmpargs[j], "-g%s", mc_addr); cmdargs[i++] = tmpargs[j++]; } } else cmdargs[i++] = "-u"; } if (do_jitter) { cmdargs[i++] = "-j"; } if (do_owd) { cmdargs[i++] = "-o"; } if (interval) { sprintf(tmpargs[j], "-i%f", interval); cmdargs[i++] = tmpargs[j++]; } if (reverse) cmdargs[i++] = "-F"; if (format) { if (format & XMITSTATS) cmdargs[i++] = "-fxmitstats"; if (format & RUNNINGTOTAL) cmdargs[i++] = "-frunningtotal"; if (format & NOPERCENTLOSS) cmdargs[i++] = "-f-percentloss"; if (format & NODROPS) cmdargs[i++] = "-f-drops"; if (format & NORETRANS) cmdargs[i++] = "-f-retrans"; if (!(format & NORETRANS) && (format & NOCWND)) cmdargs[i++] = "-f-cwnd"; if (format & PARSE) cmdargs[i++] = "-fparse"; } else { cmdargs[i++] = "-f-rtt"; } if (traceroute) cmdargs[i++] = "-xt"; if (datamss) { sprintf(tmpargs[j], "-M%d", datamss); cmdargs[i++] = tmpargs[j++]; } if (tos) { sprintf(tmpargs[j], "-c0x%Xt", tos); cmdargs[i++] = tmpargs[j++]; } if (nodelay) cmdargs[i++] = "-D"; if (ipad_stride.ip32) { sprintf(tmpargs[j], "%s+%d", host3, ipad_stride.ip32); cmdargs[i++] = tmpargs[j++]; } else cmdargs[i++] = host3; cmdargs[i] = NULL; execvp(cmd, cmdargs); if (errno == ENOENT) { strcpy(path, "/usr/local/sbin/"); strcat(path, cmd); execv(path, cmdargs); } if (errno == ENOENT) { strcpy(path, "/usr/local/bin/"); strcat(path, cmd); execv(path, cmdargs); } if (errno == ENOENT) { strcpy(path, "/usr/sbin/"); strcat(path, cmd); execv(path, cmdargs); } if (errno == ENOENT) { strcpy(path, "/sbin/"); strcat(path, cmd); execv(path, cmdargs); } if (errno == ENOENT) { strcpy(path, "/usr/etc/"); strcat(path, cmd); execv(path, cmdargs); } if ((errno == ENOENT) && (getuid() != 0) && (geteuid() != 0)) { strcpy(path, "./"); strcat(path, cmd); execv(path, cmdargs); } perror("execvp failed"); fprintf(stderr, "failed to execute %s\n", cmd); fflush(stdout); fflush(stderr); if (!inetd) exit(0); goto cleanup; } } if (traceroute && clientserver) { char path[64]; char *cmd; fflush(stdout); fflush(stderr); if (multicast) { cmd = "mtrace"; #ifdef AF_INET6 if (af == AF_INET6) cmd = "mtrace6"; #endif } else { cmd = "traceroute"; #ifdef AF_INET6 if (af == AF_INET6) cmd = "traceroute6"; #endif } if (client) { if ((pid = fork()) == (pid_t)-1) err("can't fork"); if (pid != 0) { while ((wait_pid = wait(&pidstat)) != pid) { if (wait_pid == (pid_t)-1) { if (errno == ECHILD) break; err("wait failed"); } } fflush(stdout); } else { signal(SIGINT, SIG_DFL); close(2); dup(1); i = 0; cmdargs[i++] = cmd; cmdargs[i++] = host; cmdargs[i] = NULL; execvp(cmd, cmdargs); if (errno == ENOENT) { strcpy(path, "/usr/local/sbin/"); strcat(path, cmd); execv(path, cmdargs); } if (errno == ENOENT) { strcpy(path, "/usr/local/bin/"); strcat(path, cmd); execv(path, cmdargs); } if (errno == ENOENT) { strcpy(path, "/usr/sbin/"); strcat(path, cmd); execv(path, cmdargs); } if (errno == ENOENT) { strcpy(path, "/sbin/"); strcat(path, cmd); execv(path, cmdargs); } if (errno == ENOENT) { strcpy(path, "/usr/etc/"); strcat(path, cmd); execv(path, cmdargs); } perror("execvp failed"); fprintf(stderr, "failed to execute %s\n", cmd); fflush(stdout); fflush(stderr); exit(0); } } fprintf(stdout, "\n"); if (intr) { intr = 0; fprintf(stdout, "\n"); signal(SIGINT, sigint); } if (!skip_data) { for ( stream_idx = 1; stream_idx <= nstream; stream_idx++ ) close(fd[stream_idx]); } if (client) { if ((pid = fork()) == (pid_t)-1) err("can't fork"); if (pid == 0) { while (fgets(linebuf, sizeof(linebuf), stdin) && !intr) { if (strncmp(linebuf, "DONE", 4) == 0) exit(0); fputs(linebuf, stdout); fflush(stdout); } exit(0); } signal(SIGINT, SIG_IGN); while ((wait_pid = wait(&pidstat)) != pid) { if (wait_pid == (pid_t)-1) { if (errno == ECHILD) break; err("wait failed"); } } exit(0); } else { if (!inetd) { if ((pid = fork()) == (pid_t)-1) err("can't fork"); if (pid != 0) { while ((wait_pid = wait(&pidstat)) != pid) { if (wait_pid == (pid_t)-1) { if (errno == ECHILD) break; err("wait failed"); } } fprintf(stdout, "DONE\n"); fflush(stdout); goto cleanup; } } close(2); dup(1); i = 0; cmdargs[i++] = cmd; cmdargs[i++] = host; cmdargs[i] = NULL; execvp(cmd, cmdargs); if (errno == ENOENT) { strcpy(path, "/usr/local/sbin/"); strcat(path, cmd); execv(path, cmdargs); } if (errno == ENOENT) { strcpy(path, "/usr/local/bin/"); strcat(path, cmd); execv(path, cmdargs); } if (errno == ENOENT) { strcpy(path, "/usr/sbin/"); strcat(path, cmd); execv(path, cmdargs); } if (errno == ENOENT) { strcpy(path, "/sbin/"); strcat(path, cmd); execv(path, cmdargs); } if (errno == ENOENT) { strcpy(path, "/usr/etc/"); strcat(path, cmd); execv(path, cmdargs); } perror("execvp failed"); fprintf(stderr, "failed to execute %s\n", cmd); fflush(stdout); fflush(stderr); if (!inetd) exit(0); goto cleanup; } } if (multicast) { struct sockaddr_in peer; socklen_t peerlen = sizeof(peer); struct sockaddr_in me; socklen_t melen = sizeof(me); #ifdef AF_INET6 struct sockaddr_in6 peer6; socklen_t peer6len = sizeof(peer6); struct sockaddr_in6 me6; socklen_t me6len = sizeof(me6); #endif if (mc_addr && !client) { bzero(&hints, sizeof(hints)); hints.ai_flags = AI_NUMERICHOST; if (udp) hints.ai_socktype = SOCK_DGRAM; else hints.ai_socktype = SOCK_STREAM; mcres = NULL; error_num = getaddrinfo(mc_addr, NULL, &hints, &mcres); if (error_num) { sprintf(tmpbuf, "getaddrinfo: " "bad multicast IP address: " "%s: %s", mc_addr, gai_strerror(error_num)); errno = EINVAL; err(tmpbuf); } if (mcres->ai_family == AF_INET) { struct sockaddr_in *group; struct in_addr ipv4_mcaddr; group = (struct sockaddr_in *)mcres->ai_addr; bcopy((char *)&(group->sin_addr), (char *)&ipv4_mcaddr, sizeof(struct in_addr)); if (ssm) { if (((htonl(ipv4_mcaddr.s_addr) & 0xFF000000) != (HI_MC_SSM << 24))) { sprintf(tmpbuf, "bad SSM multicast " "IP address: %s: " "use 232.x.y.z", mcgaddr); errno = EINVAL; err(tmpbuf); } } else { if (((htonl(ipv4_mcaddr.s_addr) & 0xFF000000) != (HI_MC << 24))) { sprintf(tmpbuf, "bad ASM multicast " "IP address: %s: " "use 231.x.y.z", mcgaddr); errno = EINVAL; err(tmpbuf); } } } #ifdef AF_INET6 if (mcres->ai_family == AF_INET6) { struct sockaddr_in6 *group; group = (struct sockaddr_in6 *)mcres->ai_addr; if (ssm) { if ((bcmp((char *)&(group->sin6_addr), (char *)&hi_mc6, HI_MC6_LEN - 1) != 0) || (group->sin6_addr.s6_addr[HI_MC6_LEN - 1] < 0x80)) { sprintf(tmpbuf, "bad SSM multicast " "IP address: %s: use " "ff3e::[8-f]xxx:yyyy", mcgaddr); errno = EINVAL; err(tmpbuf); } } else { if ((bcmp((char *)&(group->sin6_addr), (char *)&hi_mc6_asm, HI_MC6_ASM_LEN) != 0)) { sprintf(tmpbuf, "bad ASM multicast " "IP address: %s: use " "ff2e::wwww:xxxx:" "yyyy:zzzz", mcgaddr); errno = EINVAL; err(tmpbuf); } } } #endif mc_af = mcres->ai_family; } if (mc_af == AF_INET) { if (getpeername(fd[0], (struct sockaddr *)&peer, &peerlen) < 0) { err("getpeername"); } if (getsockname(fd[0], (struct sockaddr *)&me, &melen) < 0) { err("getsockname"); } } #ifdef AF_INET6 else if (mc_af == AF_INET6) { if (getpeername(fd[0], (struct sockaddr *)&peer6, &peer6len) < 0) { err("getpeername"); } if (getsockname(fd[0], (struct sockaddr *)&me6, &me6len) < 0) { err("getsockname"); } } #endif /* AF_INET6 */ else { err("unsupported AF"); } if (!trans) { if ((mc_af == AF_INET) && !ssm) { /* IPv4 ASM */ /* The multicast receiver must join the mc group */ if (mc_addr) { struct sockaddr_in *user_group; user_group = (struct sockaddr_in *)mcres->ai_addr; bcopy((char *)&(user_group->sin_addr.s_addr), (char *)&mc_group.imr_multiaddr.s_addr, sizeof(struct in_addr)); } else if (client && (irvers >= 50505)) { bcopy((char *)&me.sin_addr.s_addr, (char *)&mc_group.imr_multiaddr.s_addr, sizeof(struct in_addr)); } else { bcopy((char *)&peer.sin_addr.s_addr, (char *)&mc_group.imr_multiaddr.s_addr, sizeof(struct in_addr)); } if (!mc_addr) { mc_group.imr_multiaddr.s_addr &= htonl(0xFFFFFF); mc_group.imr_multiaddr.s_addr |= htonl(HI_MC << 24); } if (setsockopt(fd[1], IPPROTO_IP, IP_ADD_MEMBERSHIP, (void *)&mc_group, sizeof(mc_group)) < 0) err("setsockopt: IP_ADD_MEMBERSHIP"); if (brief <= 0) { inet_ntop(mc_af, &peer.sin_addr.s_addr, multsrc, sizeof(multsrc)); inet_ntop(mc_af, &mc_group.imr_multiaddr, multaddr, sizeof(multaddr)); if (format & PARSE) fprintf(stdout, "nuttcp%s%s: multicast_source=%s multicast_group=%s ssm=0\n", trans?"-t":"-r", ident, multsrc, multaddr); else fprintf(stdout, "nuttcp%s%s: receive from multicast source %s\n", trans?"-t":"-r", ident, multsrc); fprintf(stdout, "nuttcp%s%s: using asm on multicast group %s\n", trans?"-t":"-r", ident, multaddr); } } #ifdef AF_INET6 else if ((mc_af == AF_INET6) && !ssm) { /* IPv6 ASM */ /* The multicast receiver must join the mc group */ if (mc_addr) { struct sockaddr_in6 *user_group; user_group = (struct sockaddr_in6 *)mcres->ai_addr; bcopy((char *)&(user_group->sin6_addr), (char *)&mc6_group.ipv6mr_multiaddr, sizeof(struct in6_addr)); } else if (client) { bcopy((char *)&me6.sin6_addr, (char *)&mc6_group.ipv6mr_multiaddr, sizeof(struct in6_addr)); } else { bcopy((char *)&peer6.sin6_addr, (char *)&mc6_group.ipv6mr_multiaddr, sizeof(struct in6_addr)); } if (!mc_addr) { bcopy((char *)&hi_mc6_asm, (char *)&mc6_group.ipv6mr_multiaddr, HI_MC6_ASM_LEN); } if (setsockopt(fd[1], IPPROTO_IPV6, IPV6_JOIN_GROUP, (void *)&mc6_group, sizeof(mc6_group)) < 0) err("setsockopt: IPV6_JOIN_GROUP"); if (brief <= 0) { inet_ntop(mc_af, &peer6.sin6_addr, multsrc, sizeof(multsrc)); inet_ntop(mc_af, &mc6_group.ipv6mr_multiaddr, multaddr, sizeof(multaddr)); if (format & PARSE) fprintf(stdout, "nuttcp%s%s: multicast_source=%s multicast_group=%s ssm=0\n", trans?"-t":"-r", ident, multsrc, multaddr); else fprintf(stdout, "nuttcp%s%s: receive from multicast source %s\n", trans?"-t":"-r", ident, multsrc); fprintf(stdout, "nuttcp%s%s: using asm on multicast group %s\n", trans?"-t":"-r", ident, multaddr); } } #endif /* AF_INET6 */ #ifdef MCAST_JOIN_SOURCE_GROUP else if ((mc_af == AF_INET) && ssm) { /* IPv4 SSM */ /* multicast receiver joins the mc source group */ union sockaddr_union group_ipaddr; struct sockaddr_in *group; struct sockaddr_in *source; group = &group_ipaddr.sin; source = (struct sockaddr_in *)&group_source_req.gsr_source; group_source_req.gsr_interface = 0; /* any interface */ if (mc_addr) { struct sockaddr_in *user_group; user_group = (struct sockaddr_in *)mcres->ai_addr; bcopy((char *)user_group, (char *)group, sizeof(struct sockaddr_in)); } else if (client) { bcopy((char *)&me, (char *)group, sizeof(struct sockaddr_in)); } else { bcopy((char *)&peer, (char *)group, sizeof(struct sockaddr_in)); } bcopy((char *)&peer, (char *)source, sizeof(struct sockaddr_in)); if (!mc_addr) { group->sin_addr.s_addr &= htonl(0xFFFFFF); group->sin_addr.s_addr |= htonl(HI_MC_SSM << 24); } group_source_req.gsr_group = group_ipaddr.ss; if (setsockopt(fd[1], IPPROTO_IP, MCAST_JOIN_SOURCE_GROUP, &group_source_req, sizeof(group_source_req)) < 0) err("setsockopt: MCAST_JOIN_SOURCE_GROUP"); if (brief <= 0) { inet_ntop(mc_af, &source->sin_addr.s_addr, multsrc, sizeof(multsrc)); inet_ntop(mc_af, &group->sin_addr.s_addr, multaddr, sizeof(multaddr)); if (format & PARSE) fprintf(stdout, "nuttcp%s%s: multicast_source=%s multicast_group=%s ssm=1\n", trans?"-t":"-r", ident, multsrc, multaddr); else fprintf(stdout, "nuttcp%s%s: receive from multicast source %s\n", trans?"-t":"-r", ident, multsrc); fprintf(stdout, "nuttcp%s%s: using ssm on multicast group %s\n", trans?"-t":"-r", ident, multaddr); } } #ifdef AF_INET6 else if ((mc_af == AF_INET6) && ssm) { /* IPv6 SSM */ /* multicast receiver joins the mc source group */ struct sockaddr_in6 *group; struct sockaddr_in6 *source; group = (struct sockaddr_in6 *)&group_source_req.gsr_group; source = (struct sockaddr_in6 *)&group_source_req.gsr_source; group_source_req.gsr_interface = 0; /* any interface */ if (mc_addr) { struct sockaddr_in6 *user_group; user_group = (struct sockaddr_in6 *)mcres->ai_addr; bcopy((char *)user_group, (char *)group, sizeof(struct sockaddr_in6)); } else if (client) { bcopy((char *)&me6, (char *)group, sizeof(struct sockaddr_in6)); } else { bcopy((char *)&peer6, (char *)group, sizeof(struct sockaddr_in6)); } bcopy((char *)&peer6, (char *)source, sizeof(struct sockaddr_in6)); if (!mc_addr) { bcopy((char *)&hi_mc6, (char *)&group->sin6_addr, HI_MC6_LEN); } if (setsockopt(fd[1], IPPROTO_IPV6, MCAST_JOIN_SOURCE_GROUP, &group_source_req, sizeof(group_source_req)) < 0) err("setsockopt: MCAST_JOIN_SOURCE_GROUP"); if (brief <= 0) { inet_ntop(mc_af, &source->sin6_addr.s6_addr, multsrc, sizeof(multsrc)); inet_ntop(mc_af, &group->sin6_addr.s6_addr, multaddr, sizeof(multaddr)); if (format & PARSE) fprintf(stdout, "nuttcp%s%s: multicast_source=%s multicast_group=%s ssm=1\n", trans?"-t":"-r", ident, multsrc, multaddr); else fprintf(stdout, "nuttcp%s%s: receive from multicast source %s\n", trans?"-t":"-r", ident, multsrc); fprintf(stdout, "nuttcp%s%s: using ssm on multicast group %s\n", trans?"-t":"-r", ident, multaddr); } } #endif /* AF_INET6 */ #endif /* MCAST_JOIN_SOURCE_GROUP */ else { err("unsupported AF"); } } else { /* trans */ if (mc_af == AF_INET) { bcopy((char *)&sinhim[1].sin_addr.s_addr, (char *)&save_sinhim.sin_addr.s_addr, sizeof(struct in_addr)); } #ifdef AF_INET6 else if (mc_af == AF_INET6) { bcopy((char *)&sinhim6[1], (char *)&save_sinhim6, sizeof(struct sockaddr_in6)); } #endif if ((mc_af == AF_INET) && !ssm) { /* IPv4 ASM */ /* The multicast transmitter just sends to mc group */ if (mc_addr) { struct sockaddr_in *user_group; user_group = (struct sockaddr_in *)mcres->ai_addr; bcopy((char *)&(user_group->sin_addr.s_addr), (char *)&sinhim[1].sin_addr.s_addr, sizeof(struct in_addr)); } else if (client || (irvers < 50505)) { bcopy((char *)&me.sin_addr.s_addr, (char *)&sinhim[1].sin_addr.s_addr, sizeof(struct in_addr)); } else { bcopy((char *)&peer.sin_addr.s_addr, (char *)&sinhim[1].sin_addr.s_addr, sizeof(struct in_addr)); } if (!mc_addr) { sinhim[1].sin_addr.s_addr &= htonl(0xFFFFFF); sinhim[1].sin_addr.s_addr |= htonl(HI_MC << 24); } if (setsockopt(fd[1], IPPROTO_IP, IP_MULTICAST_TTL, (void *)&multicast, sizeof(multicast)) < 0) err("setsockopt: IP_MULTICAST_TTL"); if (brief <= 0) { inet_ntop(mc_af, &me.sin_addr.s_addr, multsrc, sizeof(multsrc)); inet_ntop(mc_af, &sinhim[1].sin_addr.s_addr, multaddr, sizeof(multaddr)); if (format & PARSE) { fprintf(stdout, "nuttcp%s%s: multicast_source=%s multicast_group=%s ssm=0\n", trans?"-t":"-r", ident, multsrc, multaddr); } else { fprintf(stdout, "nuttcp%s%s: sending from multicast source %s\n", trans?"-t":"-r", ident, multsrc); fprintf(stdout, "nuttcp%s%s: using asm on multicast group %s\n", trans?"-t":"-r", ident, multaddr); } } } #ifdef AF_INET6 else if ((mc_af == AF_INET6) && !ssm) { /* IPv6 ASM */ /* The multicast transmitter just sends to mc group */ if (mc_addr) { struct sockaddr_in6 *user_group; user_group = (struct sockaddr_in6 *)mcres->ai_addr; bcopy((char *)&(user_group->sin6_addr), (char *)&sinhim6[1].sin6_addr, sizeof(struct in6_addr)); } else if (client) { bcopy((char *)&me6.sin6_addr, (char *)&sinhim6[1].sin6_addr, sizeof(struct in6_addr)); } else { bcopy((char *)&peer6.sin6_addr, (char *)&sinhim6[1].sin6_addr, sizeof(struct in6_addr)); } if (!mc_addr) { bcopy((char *)&hi_mc6_asm, (char *)&sinhim6[1].sin6_addr, HI_MC6_ASM_LEN); } if (setsockopt(fd[1], IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (void *)&multicast, sizeof(multicast)) < 0) err("setsockopt: IPV6_MULTICAST_HOPS"); if (brief <= 0) { inet_ntop(mc_af, &me6.sin6_addr.s6_addr, multsrc, sizeof(multsrc)); inet_ntop(mc_af, &sinhim6[1].sin6_addr.s6_addr, multaddr, sizeof(multaddr)); if (format & PARSE) { fprintf(stdout, "nuttcp%s%s: multicast_source=%s multicast_group=%s ssm=0\n", trans?"-t":"-r", ident, multsrc, multaddr); } else { fprintf(stdout, "nuttcp%s%s: sending from multicast source %s\n", trans?"-t":"-r", ident, multsrc); fprintf(stdout, "nuttcp%s%s: using asm on multicast group %s\n", trans?"-t":"-r", ident, multaddr); } } } #endif /* AF_INET6 */ #ifdef MCAST_JOIN_SOURCE_GROUP else if ((mc_af == AF_INET) && ssm) { /* IPv4 SSM */ /* The multicast transmitter just sends to mc group */ if (mc_addr) { struct sockaddr_in *user_group; user_group = (struct sockaddr_in *)mcres->ai_addr; bcopy((char *)&(user_group->sin_addr.s_addr), (char *)&sinhim[1].sin_addr.s_addr, sizeof(struct in_addr)); } else if (client) { bcopy((char *)&me.sin_addr.s_addr, (char *)&sinhim[1].sin_addr.s_addr, sizeof(struct in_addr)); } else { bcopy((char *)&peer.sin_addr.s_addr, (char *)&sinhim[1].sin_addr.s_addr, sizeof(struct in_addr)); } if (!mc_addr) { sinhim[1].sin_addr.s_addr &= htonl(0xFFFFFF); sinhim[1].sin_addr.s_addr |= htonl(HI_MC_SSM << 24); } if (setsockopt(fd[1], IPPROTO_IP, IP_MULTICAST_TTL, (void *)&multicast, sizeof(multicast)) < 0) err("setsockopt: IP_MULTICAST_TTL"); if (brief <= 0) { inet_ntop(mc_af, &me.sin_addr.s_addr, multsrc, sizeof(multsrc)); inet_ntop(mc_af, &sinhim[1].sin_addr.s_addr, multaddr, sizeof(multaddr)); if (format & PARSE) fprintf(stdout, "nuttcp%s%s: multicast_source=%s multicast_group=%s ssm=1\n", trans?"-t":"-r", ident, multsrc, multaddr); else fprintf(stdout, "nuttcp%s%s: sending from multicast source %s\n", trans?"-t":"-r", ident, multsrc); fprintf(stdout, "nuttcp%s%s: using ssm on multicast group %s\n", trans?"-t":"-r", ident, multaddr); } } #ifdef AF_INET6 else if ((mc_af == AF_INET6) && ssm) { /* IPv6 SSM */ /* The multicast transmitter just sends to mc group */ if (mc_addr) { struct sockaddr_in6 *user_group; user_group = (struct sockaddr_in6 *)mcres->ai_addr; bcopy((char *)&(user_group->sin6_addr), (char *)&sinhim6[1].sin6_addr, sizeof(struct in6_addr)); } else if (client) { bcopy((char *)&me6.sin6_addr, (char *)&sinhim6[1].sin6_addr, sizeof(struct in6_addr)); } else { bcopy((char *)&peer6.sin6_addr, (char *)&sinhim6[1].sin6_addr, sizeof(struct in6_addr)); } if (!mc_addr) { bcopy((char *)&hi_mc6, (char *)&sinhim6[1].sin6_addr, HI_MC6_LEN); } if (setsockopt(fd[1], IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (void *)&multicast, sizeof(multicast)) < 0) err("setsockopt: IPV6_MULTICAST_HOPS"); if (brief <= 0) { inet_ntop(mc_af, &me6.sin6_addr.s6_addr, multsrc, sizeof(multsrc)); inet_ntop(mc_af, &sinhim6[1].sin6_addr.s6_addr, multaddr, sizeof(multaddr)); if (format & PARSE) fprintf(stdout, "nuttcp%s%s: multicast_source=%s multicast_group=%s ssm=1\n", trans?"-t":"-r", ident, multsrc, multaddr); else fprintf(stdout, "nuttcp%s%s: sending from multicast source %s\n", trans?"-t":"-r", ident, multsrc); fprintf(stdout, "nuttcp%s%s: using ssm on multicast group %s\n", trans?"-t":"-r", ident, multaddr); } } #endif /* AF_INET6 */ #endif /* MCAST_JOIN_SOURCE_GROUP */ else { err("unsupported AF"); } } if (mcres) { freeaddrinfo(mcres); mcres = NULL; } } if (trans && timeout) { itimer.it_value.tv_sec = timeout; itimer.it_value.tv_usec = (timeout - itimer.it_value.tv_sec)*1000000; itimer.it_interval.tv_sec = 0; itimer.it_interval.tv_usec = 0; signal(SIGALRM, sigalarm); if (!udp) setitimer(ITIMER_REAL, &itimer, 0); } else if (!trans && interval) { sigact.sa_handler = &sigalarm; sigemptyset(&sigact.sa_mask); sigact.sa_flags = SA_RESTART; sigaction(SIGALRM, &sigact, 0); itimer.it_value.tv_sec = interval; itimer.it_value.tv_usec = (interval - itimer.it_value.tv_sec)*1000000; itimer.it_interval.tv_sec = interval; itimer.it_interval.tv_usec = (interval - itimer.it_interval.tv_sec)*1000000; setitimer(ITIMER_REAL, &itimer, 0); if (clientserver) { chk_idle_data = (interval < idle_data_min) ? idle_data_min : interval; chk_idle_data = (chk_idle_data > idle_data_max) ? idle_data_max : chk_idle_data; } } else if (clientserver && !trans) { sigact.sa_handler = &sigalarm; sigemptyset(&sigact.sa_mask); sigact.sa_flags = SA_RESTART; sigaction(SIGALRM, &sigact, 0); if (timeout) { chk_idle_data = timeout/2; } else { if (rate != MAXRATE) chk_idle_data = (double)(nbuf*buflen) /rate/125/2; else chk_idle_data = default_idle_data; } chk_idle_data = (chk_idle_data < idle_data_min) ? idle_data_min : chk_idle_data; chk_idle_data = (chk_idle_data > idle_data_max) ? idle_data_max : chk_idle_data; itimer.it_value.tv_sec = chk_idle_data; itimer.it_value.tv_usec = (chk_idle_data - itimer.it_value.tv_sec) *1000000; itimer.it_interval.tv_sec = chk_idle_data; itimer.it_interval.tv_usec = (chk_idle_data - itimer.it_interval.tv_sec) *1000000; setitimer(ITIMER_REAL, &itimer, 0); } if (interval && clientserver && client && trans) do_poll = 1; if (irate) { pkt_time = (double)buflen/rate/125; irate_pk_usec = pkt_time*1000000; irate_pk_nsec = (pkt_time*1000000 - irate_pk_usec)*1000; pkt_time_ms = pkt_time*1000; } #ifdef DEBUG if (irate && (format & DEBUGIRATE)) { debugout = fopen(DEBUGOUTPUT, "a+"); if (debugout) fprintf(debugout, "BEGIN nuttcp debug output\n"); } #endif prep_timer(); errno = 0; stream_idx = 0; ocorrection = 0; correction = 0.0; if (do_poll) { long flags; pollfds[0].fd = fileno(ctlconn); pollfds[0].events = POLLIN | POLLPRI; pollfds[0].revents = 0; for ( i = 1; i <= nstream; i++ ) { pollfds[i].fd = fd[i]; pollfds[i].events = POLLOUT; pollfds[i].revents = 0; } flags = fcntl(0, F_GETFL, 0); if (flags < 0) err("fcntl 1"); flags |= O_NONBLOCK; if (fcntl(0, F_SETFL, flags) < 0) err("fcntl 2"); } if (sinkmode) { register int cnt = 0; if (trans) { if (udp) { strcpy(buf, "BOD0"); if (multicast) { bcopy((char *)&sinhim[1].sin_addr.s_addr, (char *)&save_mc.sin_addr.s_addr, sizeof(struct in_addr)); bcopy((char *)&save_sinhim.sin_addr.s_addr, (char *)&sinhim[1].sin_addr.s_addr, sizeof(struct in_addr)); } (void)Nwrite( fd[stream_idx + 1], buf, 4 ); /* rcvr start */ if (two_bod) { usleep(250000); strcpy(buf, "BOD1"); (void)Nwrite( fd[stream_idx + 1], buf, 4 ); /* rcvr start */ } if (multicast) { bcopy((char *)&save_mc.sin_addr.s_addr, (char *)&sinhim[1].sin_addr.s_addr, sizeof(struct in_addr)); } if (timeout) setitimer(ITIMER_REAL, &itimer, 0); prep_timer(); } bzero(buf + 8, 8); /* zero out timestamp */ nbytes += buflen; if (do_poll && (format & DEBUGPOLL)) { fprintf(stdout, "do_poll is set\n"); fflush(stdout); } if (udplossinfo) bcopy(&nbytes, buf + 24, 8); if (!udp && !(format & NORETRANS) && (nstream == 1) && ((retransinfo == 1) || ((retransinfo >= 2) && (force_retrans >= retransinfo)))) { uint32_t tmp; if (client) { if (trans || send_retrans) do_retrans = 1; if (trans) send_retrans = 1; if (!udp) bzero(buf + 24, 8); } else { if (retransinfo == 1) tmp = 0x5254524Eu; /* "RTRN" */ else tmp = 0x48525452u; /* "HRTR" */ bcopy(&nretrans[1], buf + 24, 4); bcopy(&tmp, buf + 28, 4); do_retrans = 0; } } else { send_retrans = 0; do_retrans = 0; if (!udp) bzero(buf + 24, 8); } if (!udp && !(format & NOCWND) && (nstream == 1) && cwndinfo) { if (client) { if (trans || send_cwnd) do_cwnd = 1; if (trans) send_cwnd = 1; } else { do_cwnd = 0; } } else { send_cwnd = 0; do_cwnd = 0; } if (nbuf == INT_MAX) nbuf = ULLONG_MAX; if (!client) { /* check if client went away */ pollfds[0].fd = fileno(ctlconn); save_events = pollfds[0].events; pollfds[0].events = POLLIN | POLLPRI; pollfds[0].revents = 0; if ((poll(pollfds, 1, 0) > 0) && (pollfds[0].revents & (POLLIN | POLLPRI))) { nbuf = 0; intr = 1; } pollfds[0].events = save_events; } while (nbuf-- && ((cnt = Nwrite(fd[stream_idx + 1], buf, buflen)) == buflen) && !intr) { if (clientserver && ((nbuf & 0x3FF) == 0)) { if (!client) { /* check if client went away */ pollfds[0].fd = fileno(ctlconn); save_events = pollfds[0].events; pollfds[0].events = POLLIN | POLLPRI; pollfds[0].revents = 0; if ((poll(pollfds, 1, 0) > 0) && (pollfds[0].revents & (POLLIN | POLLPRI))) intr = 1; pollfds[0].events = save_events; } else if (handle_urg) { /* check for urgent TCP data * on control connection */ pollfds[0].fd = fileno(ctlconn); save_events = pollfds[0].events; pollfds[0].events = POLLPRI; pollfds[0].revents = 0; if ((poll(pollfds, 1, 0) > 0) && (pollfds[0].revents & POLLPRI)) { tmpbuf[0] = '\0'; if ((recv(fd[0], tmpbuf, 1, MSG_OOB) == -1) && (errno == EINVAL)) recv(fd[0], tmpbuf, 1, 0); if (tmpbuf[0] == 'A') intr = 1; else err("recv urgent data"); } pollfds[0].events = save_events; } } nbytes += buflen; cnt = 0; if (udplossinfo) bcopy(&nbytes, buf + 24, 8); if (send_retrans) { nretrans[1] = get_retrans( fd[stream_idx + 1], &tcpinf); nretrans[1] -= iretrans[1]; bcopy(&nretrans[1], buf + 24, 4); } #if defined(linux) if (send_cwnd) { cwnd[1] = tcpinf.tcpinfo_snd_cwnd *datamss/1024; bcopy(&cwnd[1], buf + 28, 4); } #endif stream_idx++; stream_idx = stream_idx % nstream; if (do_poll && ((pollst = poll(pollfds, nstream + 1, 0)) > 0) && (pollfds[0].revents & (POLLIN | POLLPRI)) && !intr) { /* check for server output */ #ifdef DEBUG if (format & DEBUGPOLL) { fprintf(stdout, "got something %d: ", i); for ( i = 0; i < nstream + 1; i++ ) { if (pollfds[i].revents & POLLIN) { fprintf(stdout, " rfd %d", pollfds[i].fd); } if (pollfds[i].revents & POLLPRI) { fprintf(stdout, " pfd %d", pollfds[i].fd); } if (pollfds[i].revents & POLLOUT) { fprintf(stdout, " wfd %d", pollfds[i].fd); } if (pollfds[i].revents & POLLERR) { fprintf(stdout, " xfd %d", pollfds[i].fd); } if (pollfds[i].revents & POLLHUP) { fprintf(stdout, " hfd %d", pollfds[i].fd); } if (pollfds[i].revents & POLLNVAL) { fprintf(stdout, " nfd %d", pollfds[i].fd); } } fprintf(stdout, "\n"); fflush(stdout); } if (format & DEBUGPOLL) { fprintf(stdout, "got server output: %s", intervalbuf); fflush(stdout); } #endif while (fgets(intervalbuf, sizeof(intervalbuf), stdin)) { if (strncmp(intervalbuf, "DONE", 4) == 0) { if (format & DEBUGPOLL) { fprintf(stdout, "got DONE\n"); fflush(stdout); } got_done = 1; intr = 1; do_poll = 0; break; } else if (strncmp(intervalbuf, "nuttcp-r", 8) == 0) { if ((brief <= 0) || strstr(intervalbuf, "Warning") || strstr(intervalbuf, "Error") || strstr(intervalbuf, "Debug")) { if (*ident) { fputs("nuttcp-r", stdout); fputs(ident, stdout); fputs(intervalbuf + 8, stdout); } else fputs(intervalbuf, stdout); fflush(stdout); } } else { if (*ident) fprintf(stdout, "%s: ", ident + 1); cp1 = intervalbuf + strlen(intervalbuf) - 1; /* ugly kludge to get rid of * server "0 retrans" info at * start of transfer for small * interval reports - * hopefully it won't be * necessary to also check * at end of transfer when * processing server output */ if (!got_0retrans) { if (format & PARSE) { if ((cp2 = strstr( intervalbuf, "host-" "retrans"))) *(cp2 - 1) = '\0'; else if ((cp2 = strstr( intervalbuf, "retrans"))) *(cp2 - 1) = '\0'; else { *cp1 = '\0'; got_0retrans = 1; } } else if (strstr(intervalbuf, "KB-cwnd")) { if (strstr(intervalbuf, "host-retrans")) *(cp1 - 34) = '\0'; else if (strstr( intervalbuf, "retrans")) *(cp1 - 29) = '\0'; else { *cp1 = '\0'; got_0retrans = 1; } } else { if (strstr(intervalbuf, "host-retrans")) *(cp1 - 19) = '\0'; else if (strstr( intervalbuf, "retrans")) *(cp1 - 14) = '\0'; else { *cp1 = '\0'; got_0retrans = 1; } } } else { *cp1 = '\0'; } if (do_retrans) { cp1 = strstr(intervalbuf, "Mbps") + 4; ch = '\0'; if (cp1) { if (format & PARSE) { cp1 = strchr(cp1, '.'); if (cp1) cp1 += 5; } ch = *cp1; } if (ch) *cp1 = '\0'; } fputs(intervalbuf, stdout); if (do_retrans && sinkmode) { nretrans[1] = get_retrans(fd[stream_idx + 1], &tcpinf); nretrans[1] -= iretrans[1]; if (format & PARSE) fprintf(stdout, P_RETRANS_FMT_INTERVAL, (retransinfo == 1) ? "" : "host-", (nretrans[1] - pretrans)); else fprintf(stdout, RETRANS_FMT_INTERVAL, (nretrans[1] - pretrans), (retransinfo == 1) ? "" : "host-"); pretrans = nretrans[1]; } #if defined(linux) if (do_cwnd && sinkmode) { cwnd[1] = tcpinf.tcpinfo_snd_cwnd *datamss/1024; if (format & PARSE) fprintf(stdout, P_CWND_FMT_INTERVAL, cwnd[1]); else fprintf(stdout, CWND_FMT_INTERVAL, cwnd[1]); } #endif if (do_retrans && cp1 && ch) { *cp1 = ch; fputs(cp1, stdout); } fprintf(stdout, "\n"); fflush(stdout); } } } if (do_poll && (pollst < 0)) { if (errno == EINTR) break; err("poll"); } } nbytes -= buflen; if (intr && (cnt > 0)) nbytes += cnt; if (udp) { if (multicast) bcopy((char *)&save_sinhim.sin_addr.s_addr, (char *)&sinhim[1].sin_addr.s_addr, sizeof(struct in_addr)); strcpy(buf, "EOD0"); (void)Nwrite( fd[stream_idx + 1], buf, 4 ); /* rcvr end */ } } else { first_read = 1; first_jitter = 1; first_jitteri = 1; nowd = 0; nowdi = 0; owd_min = 1000000.0; owd_mini = 1000000.0; owd_max = -1000000.0; owd_maxi = -1000000.0; owd_avg = 0.0; owd_avgi = 0.0; need_swap = 0; bzero(buf + 24, 8); if (udp) { ntbytesc = 0; got_eod0 = 0; while (((cnt=Nread(fd[stream_idx + 1], buf, buflen)) > 0) && !intr) { if (cnt <= 4) { if (strncmp(buf, "EOD0", 4) == 0) { get_timeofday(&time_eod0, (struct timezone *)0); got_eod0 = 1; done = 1; continue; } if (strncmp(buf, "EOD", 3) == 0) { ocorrection = buf[3] - '0'; get_timeofday(&time_eod, (struct timezone *)0); done = 1; break; /* "EOF" */ } if (strncmp(buf, "BOD", 3) == 0) { if (two_bod && (buf[3] == '0')) continue; if (interval) setitimer(ITIMER_REAL, &itimer, 0); prep_timer(); got_begin = 1; continue; } break; } else if (!got_begin) { if (interval) setitimer(ITIMER_REAL, &itimer, 0); prep_timer(); got_begin = 1; } else if (got_eod0) { /* got data after EOD0, so * extend EOD0 time */ get_timeofday(&time_eod0, (struct timezone *)0); } if (!got_begin) continue; nbytes += cnt; cnt = 0; /* problematic if the interval timer * goes off right here */ if (udplossinfo) { if (first_read) { bcopy(buf + 24, &ntbytesc, 8); first_read = 0; if (ntbytesc > 0x100000000ull) need_swap = 1; if (!need_swap) { stream_idx++; stream_idx = stream_idx % nstream; continue; } } if (!need_swap) bcopy(buf + 24, &ntbytesc, 8); else { cp1 = (char *)&ntbytesc; cp2 = buf + 31; for ( i = 0; i < 8; i++ ) *cp1++ = *cp2--; } } if (do_jitter) { if (first_jitter) { get_timeofday( &timepkr, (struct timezone *)0); timepkri = timepkr; first_jitter = 0; first_jitteri = 0; ntbytescp = ntbytesc; ntbytescpi = ntbytesc; jitter = 0.0; jitteri = 0.0; njitter = 0; njitteri = 0; jitter_min = 1000000.0; jitter_mini = 1000000.0; jitter_max = -1000000.0; jitter_maxi = -1000000.0; jitter_avg = 0.0; jitter_avgi = 0.0; #ifdef DEBUG if (clientserver && client && (format & DEBUGJITTER)) fprintf(stdout, "pkt_time_ms" " = %6.3f ms\n", pkt_time_ms); #endif stream_idx++; stream_idx = stream_idx % nstream; continue; } /* formula for jitter is from * RFC1889 - note synchronized * clocks are not required since * source packet delta time is * known (pkt_time_ms) * * D(i,j)=(Rj-Ri)-(Sj-Si) * =(Rj-Sj)-(Ri-Si) * J=J+(|D(i-1,i)|-J)/16 * * for nuttcp we just use the raw * absolute value of the delta * * J=|D(i-1,i)| */ if (!do_owd) { get_timeofday(&timerx, (struct timezone *)0); } if (do_jitter & JITTER_IGNORE_OOO) { /* first check that packet * is next in sequence */ if (udplossinfo && (ntbytescp + buflen) != ntbytesc) { ntbytescp = ntbytesc; ntbytescpi = ntbytesc; timepkr = timerx; timepkri = timerx; stream_idx++; stream_idx = stream_idx % nstream; continue; } } tvsub( &timed, &timerx, &timepkr ); pkt_delta = timed.tv_sec*1000 + ((double)timed.tv_usec) / 1000; pkt_delta -= pkt_time_ms; if (pkt_delta >= 0) jitter = pkt_delta; else jitter = -pkt_delta; njitter++; if (jitter < jitter_min) jitter_min = jitter; if (jitter > jitter_max) jitter_max = jitter; jitter_avg += jitter; #ifdef DEBUG if (clientserver && client && (format & DEBUGJITTER)) fprintf(stdout, "pkt_delta = %6.3f ms, " "jitter = %9.6f ms\n", pkt_delta, jitter); #endif timepkr = timerx; ntbytescp = ntbytesc; if (!interval) { stream_idx++; stream_idx = stream_idx % nstream; continue; } if (first_jitteri) { get_timeofday( &timepkri, (struct timezone *)0); first_jitteri = 0; ntbytescpi = ntbytesc; jitteri = 0.0; njitteri = 0; jitter_mini = 1000000.0; jitter_maxi = -1000000.0; jitter_avgi = 0.0; stream_idx++; stream_idx = stream_idx % nstream; continue; } tvsub( &timed, &timerx, &timepkri ); pkt_delta = timed.tv_sec*1000 + ((double)timed.tv_usec) / 1000; pkt_delta -= pkt_time_ms; if (pkt_delta >= 0) jitteri = pkt_delta; else jitteri = -pkt_delta; njitteri++; if (jitteri < jitter_mini) jitter_mini = jitteri; if (jitteri > jitter_maxi) jitter_maxi = jitteri; jitter_avgi += jitteri; timepkri = timerx; ntbytescpi = ntbytesc; } stream_idx++; stream_idx = stream_idx % nstream; } if (intr && (cnt > 0)) nbytes += cnt; if (got_eod0) { tvsub( &timed, &time_eod, &time_eod0 ); correction = timed.tv_sec + ((double)timed.tv_usec) / 1000000; } } else { while (((cnt=Nread(fd[stream_idx + 1], buf, buflen)) > 0) && !intr) { nbytes += cnt; cnt = 0; if (first_read) { if (interval && !(format & NORETRANS)) { uint32_t tmp; first_read = 0; bcopy(buf + 24, &nretrans[1], 4); bcopy(buf + 28, &tmp, 4); if (tmp == 0x5254524Eu) { /* "RTRN" */ retransinfo = 1; #if defined(linux) cwndinfo = 1; #endif b_flag = 1; } else if (tmp == 0x48525452u) { /* "HRTR" */ retransinfo = 2; cwndinfo = 0; read_cwnd = 0; b_flag = 1; } else if (tmp == 0x4E525452u) { /* "NRTR" */ need_swap = 1; retransinfo = 1; #if defined(linux) cwndinfo = 1; #endif b_flag = 1; } else if (tmp == 0x52545248u) { /* "RTRH" */ need_swap = 1; retransinfo = 2; cwndinfo = 0; read_cwnd = 0; b_flag = 1; } else { retransinfo = -1; cwndinfo = 0; read_retrans = 0; read_cwnd = 0; } if (format & NOCWND) { cwndinfo = 0; read_cwnd = 0; } } else { read_retrans = 0; read_cwnd = 0; } } if (read_retrans) { if (!need_swap) bcopy(buf + 24, &nretrans[1], 4); else { cp1 = (char *)&nretrans[1]; cp2 = buf + 27; for ( i = 0; i < 4; i++ ) *cp1++ = *cp2--; } } if (read_cwnd) { if (!need_swap) bcopy(buf + 28, &cwnd[1], 4); else { cp1 = (char *)&cwnd[1]; cp2 = buf + 31; for ( i = 0; i < 4; i++ ) *cp1++ = *cp2--; } } stream_idx++; stream_idx = stream_idx % nstream; } if (intr && (cnt > 0)) nbytes += cnt; } } } else { register int cnt; if (trans) { #if defined(linux) struct stat instat; if (fstat(savestdin, &instat) == 0) { if (!S_ISREG(instat.st_mode)) { zerocopy = 0; directio = 0; } } else { zerocopy = 0; directio = 0; } if (directio) { flags = fcntl(savestdin, F_GETFL, 0); if (flags < 0) errmes("fcntl get O_DIRECT"); else { flags |= O_DIRECT; if (fcntl(savestdin, F_SETFL, flags) < 0) errmes("fcntl set O_DIRECT"); } } if (zerocopy) { while (nbuf-- && ((cnt=sendfile(fd[stream_idx + 1], savestdin, (off_t *)&nbytes, buflen)) > 0)) { cnt = 0; stream_idx++; stream_idx = stream_idx % nstream; } } else #endif { while (nbuf-- && ((cnt=read(savestdin, buf, buflen)) > 0) && (Nwrite(fd[stream_idx + 1], buf, cnt) == cnt)) { nbytes += cnt; cnt = 0; stream_idx++; stream_idx = stream_idx % nstream; } } if (udp) { strcpy(buf, "EOD0"); (void)Nwrite( fd[stream_idx + 1], buf, 4 ); /* rcvr end */ } } else { #if defined(linux) struct stat outstat; if (fstat(savestdout, &outstat) == 0) { if (!S_ISREG(outstat.st_mode)) directio = 0; } else directio = 0; if (directio) { flags = fcntl(savestdout, F_GETFL, 0); if (flags < 0) errmes("fcntl get O_DIRECT"); else { flags |= O_DIRECT; if (fcntl(savestdout, F_SETFL, flags) < 0) errmes("fcntl set O_DIRECT"); } } #endif while ((cnt=Nread(fd[stream_idx + 1], buf, buflen)) > 0 && ((cnt != 4) || strncmp(buf, "EOD", 3) != 0) && mwrite(savestdout, buf, cnt, cnt != buflen) == cnt) { nbytes += cnt; cnt = 0; stream_idx++; stream_idx = stream_idx % nstream; } } } if (errno && (errno != EAGAIN)) { if ((errno != EINTR) && #ifdef ERESTART (errno != ERESTART) && #endif (!clientserver || client)) err("IO"); } itimer.it_value.tv_sec = 0; itimer.it_value.tv_usec = 0; itimer.it_interval.tv_sec = 0; itimer.it_interval.tv_usec = 0; setitimer(ITIMER_REAL, &itimer, 0); done = 1; (void)read_timer(stats, sizeof(stats)); if (udp&&trans) { usleep(500000); strcpy(buf, "EOD1"); (void)Nwrite( fd[stream_idx + 1], buf, 4 ); /* rcvr end */ stream_idx++; stream_idx = stream_idx % nstream; usleep(500000); strcpy(buf, "EOD2"); (void)Nwrite( fd[stream_idx + 1], buf, 4 ); /* rcvr end */ stream_idx++; stream_idx = stream_idx % nstream; usleep(500000); strcpy(buf, "EOD3"); (void)Nwrite( fd[stream_idx + 1], buf, 4 ); /* rcvr end */ stream_idx++; stream_idx = stream_idx % nstream; usleep(500000); strcpy(buf, "EOD4"); (void)Nwrite( fd[stream_idx + 1], buf, 4 ); /* rcvr end */ stream_idx++; stream_idx = stream_idx % nstream; } if (!udp && trans && (format & DEBUGRETRANS)) { sretrans = get_retrans(-1, &tcpinf); fprintf(stdout, "before closing system retrans = %d\n", sretrans); } #ifdef DEBUG if (clientserver && client && !trans && do_jitter && njitter && (format & DEBUGJITTER)) { fprintf(stdout, "njitter = %lld\n", njitter); fprintf(stdout, "jitter_min = %9.6f ms\n", jitter_min); fprintf(stdout, "jitter_max = %9.6f ms\n", jitter_max); fprintf(stdout, "jitter_avg = %9.6f ms\n", jitter_avg/njitter); } #endif for ( stream_idx = 1; stream_idx <= nstream; stream_idx++ ) { if (!udp && trans) { #if defined(linux) && defined(TCPI_OPT_TIMESTAMPS) && !defined(BROKEN_UNACKED) /* if -DBROKEN_UNACKED skip check for unACKed data * (workaround motivated by possible bug encountered * on a Suse Linux 10.1 system) */ struct timeval timeunsent, timec, timed; double xmitrate; int unsent, xmitunsent; long flags; optlen = sizeof(tcpinf); if (getsockopt(fd[stream_idx], SOL_TCP, TCP_INFO, (void *)&tcpinf, &optlen) < 0) { mes("couldn't collect TCP info\n"); retransinfo = -1; cwndinfo = 0; } if (ioctl(fd[stream_idx], SIOCOUTQ, &unsent) < 0) { mes("couldn't get SIOCOUTQ value\n"); unsent = -1; } get_timeofday(&timeunsent, (struct timezone *)0); realtd = 0.0; xmitrate = ((double)nbytes-(double)unsent)/realt; xmitunsent = (double)unsent/xmitrate; xmitunsent = 2*xmitunsent + MAX_EOT_WAIT_SEC; if (clientserver && client) { reading_srvr_info = 1; pollfds[0].fd = fileno(ctlconn); pollfds[0].events = POLLIN | POLLPRI; pollfds[0].revents = 0; flags = fcntl(0, F_GETFL, 0); if (flags < 0) err("fcntl 1"); flags |= O_NONBLOCK; if (fcntl(0, F_SETFL, flags) < 0) err("fcntl 2"); itimer.it_value.tv_sec = xmitunsent + 10; itimer.it_value.tv_usec = 0; itimer.it_interval.tv_sec = 0; itimer.it_interval.tv_usec = 0; setitimer(ITIMER_REAL, &itimer, 0); } while ((unsent > 0) && (realtd < xmitunsent)) { if (clientserver && client && ((pollst = poll(pollfds, 1, 0)) > 0) && (pollfds[0].revents & (POLLIN | POLLPRI)) && !got_done) { /* check for server output */ while (fgets(intervalbuf, sizeof(intervalbuf), stdin)) { setitimer(ITIMER_REAL, &itimer, 0); get_timeofday(&timeunsent, (struct timezone *)0); if (strncmp(intervalbuf, "DONE", 4) == 0) { if (format & DEBUGPOLL) { fprintf(stdout, "got DONE\n"); fflush(stdout); } got_done = 1; intr = 1; break; } else if (strncmp(intervalbuf, "nuttcp-", 7) == 0) { if ((brief <= 0) || strstr(intervalbuf, "Warning") || strstr(intervalbuf, "Error") || strstr(intervalbuf, "Debug")) { if (*ident) { fputs("nuttcp", stdout); fputs(trans ? "-r" : "-t", stdout); fputs(ident, stdout); fputs(intervalbuf + 8, stdout); } else fputs(intervalbuf, stdout); fflush(stdout); } if (strstr(intervalbuf, "Error")) exit(1); } else { if (*ident) fprintf(stdout, "%s: ", ident + 1); intervalbuf[strlen(intervalbuf) - 1] = '\0'; if (do_retrans) { cp1 = strstr(intervalbuf, "Mbps") + 4; ch = '\0'; if (cp1) { if (format & PARSE) { cp1 = strchr(cp1, '.'); if (cp1) cp1 += 5; } ch = *cp1; } if (ch) *cp1 = '\0'; } fputs(intervalbuf, stdout); if (do_retrans && sinkmode && (nstream == 1)) { nretrans[1] = get_retrans( fd[stream_idx], &tcpinf); nretrans[1] -= iretrans[1]; if (format & PARSE) fprintf(stdout, P_RETRANS_FMT_INTERVAL, (retransinfo == 1) ? "" : "host-", (nretrans[1] - pretrans)); else fprintf(stdout, RETRANS_FMT_INTERVAL, (nretrans[1] - pretrans), (retransinfo == 1) ? "" : "host-"); pretrans = nretrans[1]; } if (do_cwnd && sinkmode && (nstream == 1)) { cwnd[1] = tcpinf.tcpinfo_snd_cwnd *datamss/1024; if (format & PARSE) fprintf(stdout, P_CWND_FMT_INTERVAL, cwnd[1]); else fprintf(stdout, CWND_FMT_INTERVAL, cwnd[1]); } if (do_retrans && cp1 && ch) { *cp1 = ch; fputs(cp1, stdout); } fprintf(stdout, "\n"); fflush(stdout); } } } if (format & DEBUGRETRANS) print_tcpinfo(); if (format & DEBUGRETRANS) usleep(100000); else usleep(1000); optlen = sizeof(tcpinf); if (getsockopt(fd[stream_idx], SOL_TCP, TCP_INFO, (void *)&tcpinf, &optlen) < 0) { mes("couldn't collect TCP info\n"); retransinfo = -1; cwndinfo = 0; } if (ioctl(fd[stream_idx], SIOCOUTQ, &unsent) < 0) { mes("couldn't get SIOCOUTQ value\n"); unsent = -1; } get_timeofday(&timec, (struct timezone *)0); tvsub(&timed, &timec, &timeunsent); realtd = timed.tv_sec + ((double)timed.tv_usec) / 1000000; } if (clientserver && client) { reading_srvr_info = 0; flags = fcntl(0, F_GETFL, 0); if (flags < 0) err("fcntl 1"); flags &= ~O_NONBLOCK; if (fcntl(0, F_SETFL, flags) < 0) err("fcntl 2"); itimer.it_value.tv_sec = 0; itimer.it_value.tv_usec = 0; setitimer(ITIMER_REAL, &itimer, 0); } if (getsockopt(fd[stream_idx], SOL_TCP, TCP_INFO, (void *)&tcpinf, &optlen) < 0) { mes("couldn't collect TCP info\n"); retransinfo = -1; cwndinfo = 0; } if (unsent > 0) { /* assume receiver went away */ if (clientserver && client) { mes("Error: timeout while draining " "socket send queue"); exit(1); } goto cleanup; } if (format & DEBUGRETRANS) print_tcpinfo(); #endif if (retransinfo > 0) { if ((stream_idx == 1) || (retransinfo == 1)) { nretrans[stream_idx] = get_retrans(fd[stream_idx], &tcpinf); nretrans[stream_idx] -= iretrans[stream_idx]; #if defined(linux) if (retransinfo == 1) cwnd[stream_idx] = tcpinf.tcpinfo_snd_cwnd *datamss/1024; #endif } } } } if (!udp && trans && (format & DEBUGRETRANS)) { sretrans = get_retrans(-1, &tcpinf); fprintf(stdout, "after closing system retrans = %d\n", sretrans); } if (interval && clientserver && client && do_retrans && !got_done) { /* don't fully close data channels yet since there * may be some straggler interval reports to which we * will need to append retrans info, so just shutdown() * for writing for now */ for ( stream_idx = 1; stream_idx <= nstream; stream_idx++ ) shutdown(fd[stream_idx], SHUT_WR); } else close_data_channels(); if (interval && clientserver && !client && !trans) { fprintf(stdout, "DONE\n"); fflush(stdout); } if (cput <= 0.0) cput = 0.000001; if (realt <= 0.0) realt = 0.000001; if (udp && !trans) { if (got_eod0) realt -= correction; else realt -= ocorrection * 0.5; } sprintf(srvrbuf, "%.4f", (double)nbytes/1024/1024); sscanf(srvrbuf, "%lf", &MB); if (clientserver && client) reading_srvr_info = 1; if (interval && clientserver && client && trans && !got_done) { long flags; if (format & DEBUGPOLL) { fprintf(stdout, "getting rest of server output\n"); fflush(stdout); } flags = fcntl(0, F_GETFL, 0); if (flags < 0) err("fcntl 3"); flags &= ~O_NONBLOCK; if (fcntl(0, F_SETFL, flags) < 0) err("fcntl 4"); itimer.it_value.tv_sec = SRVR_INFO_TIMEOUT; itimer.it_value.tv_usec = 0; itimer.it_interval.tv_sec = 0; itimer.it_interval.tv_usec = 0; setitimer(ITIMER_REAL, &itimer, 0); while (fgets(intervalbuf, sizeof(intervalbuf), stdin)) { setitimer(ITIMER_REAL, &itimer, 0); if (strncmp(intervalbuf, "DONE", 4) == 0) { if (format & DEBUGPOLL) { fprintf(stdout, "got DONE 2\n"); fflush(stdout); } break; } if ((!strstr(intervalbuf, " MB / ") || !strstr(intervalbuf, " sec = ")) && (brief > 0)) continue; if (*ident) fprintf(stdout, "%s: ", ident + 1); intervalbuf[strlen(intervalbuf) - 1] = '\0'; if (do_retrans) { cp1 = strstr(intervalbuf, "Mbps") + 4; ch = '\0'; if (cp1) { if (format & PARSE) { cp1 = strchr(cp1, '.'); if (cp1) cp1 += 5; } ch = *cp1; } if (ch) *cp1 = '\0'; } fputs(intervalbuf, stdout); if (do_retrans && sinkmode) { nretrans[1] = get_retrans(fd[1], &tcpinf); nretrans[1] -= iretrans[1]; if (format & PARSE) fprintf(stdout, P_RETRANS_FMT_INTERVAL, (retransinfo == 1) ? "" : "host-", (nretrans[1] - pretrans)); else fprintf(stdout, RETRANS_FMT_INTERVAL, (nretrans[1] - pretrans), (retransinfo == 1) ? "" : "host-"); pretrans = nretrans[1]; } #if defined(linux) if (do_cwnd && sinkmode) { cwnd[1] = tcpinf.tcpinfo_snd_cwnd *datamss/1024; if (format & PARSE) fprintf(stdout, P_CWND_FMT_INTERVAL, cwnd[1]); else fprintf(stdout, CWND_FMT_INTERVAL, cwnd[1]); } #endif if (do_retrans && cp1 && ch) { *cp1 = ch; fputs(cp1, stdout); } fprintf(stdout, "\n"); fflush(stdout); } itimer.it_value.tv_sec = 0; itimer.it_value.tv_usec = 0; setitimer(ITIMER_REAL, &itimer, 0); } if (interval && clientserver && client && do_retrans) { /* it's OK to fully close the data channels now */ close_data_channels(); } if (clientserver && client) { itimer.it_value.tv_sec = SRVR_INFO_TIMEOUT; itimer.it_value.tv_usec = 0; setitimer(ITIMER_REAL, &itimer, 0); cp1 = srvrbuf; got_srvr_retrans = 0; got_srvr_cwnd = 0; while (fgets(cp1, sizeof(srvrbuf) - (cp1 - srvrbuf), stdin)) { setitimer(ITIMER_REAL, &itimer, 0); if (*(cp1 + strlen(cp1) - 1) != '\n') { *cp1 = '\0'; break; } if (strstr(cp1, "real") && strstr(cp1, "seconds")) { strcpy(fmt, "nuttcp-%*c: "); if (format & PARSE) strcat(fmt, P_PERF_FMT_IN); else strcat(fmt, PERF_FMT_IN); sscanf(cp1, fmt, &srvr_MB, &srvr_realt, &srvr_KBps, &srvr_Mbps); if (trans && udp) { strncpy(tmpbuf, cp1, 256); *(tmpbuf + 256) = '\0'; if (strncmp(tmpbuf, "nuttcp-r", 8) == 0) sprintf(cp1, "nuttcp-r%s%s", ident, tmpbuf + 8); cp1 += strlen(cp1); cp2 = cp1; sprintf(cp2, "nuttcp-r:"); cp2 += 9; if (format & PARSE) strcpy(fmt, P_DROP_FMT); else strcpy(fmt, DROP_FMT); sprintf(cp2, fmt, (int64_t)(((MB - srvr_MB) *1024*1024) /buflen + 0.5), (uint64_t)((MB*1024*1024) /buflen + 0.5)); cp2 += strlen(cp2); fractloss = ((MB != 0.0) ? 1 - srvr_MB/MB : 0.0); if (format & PARSE) strcpy(fmt, P_LOSS_FMT); else if ((fractloss != 0.0) && (fractloss < 0.001)) strcpy(fmt, LOSS_FMT5); else strcpy(fmt, LOSS_FMT); sprintf(cp2, fmt, fractloss * 100); cp2 += strlen(cp2); sprintf(cp2, "\n"); } } else if (strstr(cp1, "sys")) { strcpy(fmt, "nuttcp-%*c: "); if (format & PARSE) { strcat(fmt, "stats=cpu "); strcat(fmt, P_CPU_STATS_FMT_IN2); } else strcat(fmt, CPU_STATS_FMT_IN2); if (sscanf(cp1, fmt, &srvr_cpu_util) != 7) { strcpy(fmt, "nuttcp-%*c: "); if (format & PARSE) { strcat(fmt, "stats=cpu "); strcat(fmt, P_CPU_STATS_FMT_IN); } else strcat(fmt, CPU_STATS_FMT_IN); sscanf(cp1, fmt, &srvr_cpu_util); } } else if ((cp2 = strstr(cp1, "retrans"))) { got_srvr_retrans = 1; retransinfo = 1; if (strstr(cp1, "host-retrans")) retransinfo = 2; if (format & PARSE) sscanf(cp2, P_RETRANS_FMT_IN, &total_retrans); else sscanf(cp2, RETRANS_FMT_IN, &total_retrans); if (format & PARSE) { if ((cp2 = strstr(cp2, "retrans_by_stream="))) cp2 += 18; else cp2 = NULL; } else { if ((cp2 = strstr(cp2, "( "))) cp2 += 2; else cp2 = NULL; } if (cp2) { sscanf(cp2, "%d", &nretrans[1]); stream_idx = 2; while ((stream_idx <= nstream) && (cp2 = strchr(cp2, '+'))) { cp2++; sscanf(cp2, "%d", &nretrans[stream_idx]); stream_idx++; } } /* below is for compatibility with 6.0.x beta */ if ((cp2 = strstr(cp1, "RTT"))) { if (format & PARSE) sscanf(cp2, P_RTT_FMT_IN, &rtt); else sscanf(cp2, RTT_FMT_INB, &rtt); } if ((cp2 = strstr(cp1, "cwnd"))) { got_srvr_cwnd = 1; cwndinfo = 1; if (format & PARSE) sscanf(cp2, P_CWND_FMT_IN, &total_snd_cwnd); else sscanf(cp2, CWND_FMT_IN, &total_snd_cwnd); if (format & PARSE) { if ((cp2 = strstr(cp2, "cwnd_by_stream="))) cp2 += 15; else cp2 = NULL; } else { if ((cp2 = strstr(cp2, "( "))) cp2 += 2; else cp2 = NULL; } if (cp2) { sscanf(cp2, "%d", &cwnd[1]); stream_idx = 2; while ((stream_idx <= nstream) && (cp2 = strchr(cp2, '+'))) { cp2++; sscanf(cp2, "%d", &cwnd[stream_idx]); stream_idx++; } } } } else if ((cp2 = strstr(cp1, "RTT")) || (cp2 = strstr(cp1, "rtt"))) { if (format & PARSE) sscanf(cp2, P_RTT_FMT_IN, &rtt); else sscanf(cp2, RTT_FMT_IN, &rtt); } else if ((cp2 = strstr(cp1, "jitter"))) { if (format & PARSE) sscanf(cp2, P_JITTER_FMT_IN, &jitter_min, &jitter_avg, &jitter_max); else sscanf(cp2, JITTER_FMT_IN, &jitter_min, &jitter_avg, &jitter_max); njitter = 1; } else if ((cp2 = strstr(cp1, "OWD"))) { if (format & PARSE) sscanf(cp2, P_OWD_FMT_IN, &owd_min, &owd_avg, &owd_max); else sscanf(cp2, OWD_FMT_IN, &owd_min, &owd_avg, &owd_max); nowd = 1; } else if ((strstr(cp1, "KB/cpu")) && !verbose) continue; strncpy(tmpbuf, cp1, 256); *(tmpbuf + 256) = '\0'; if (strncmp(tmpbuf, "nuttcp-", 7) == 0) sprintf(cp1, "nuttcp-%c%s%s", tmpbuf[7], ident, tmpbuf + 8); if ((strstr(cp1, "Warning") || strstr(cp1, "Error") || strstr(cp1, "Debug")) && (brief > 0)) { fputs(cp1, stdout); fflush(stdout); } cp1 += strlen(cp1); } itimer.it_value.tv_sec = 0; itimer.it_value.tv_usec = 0; setitimer(ITIMER_REAL, &itimer, 0); got_srvr_output = 1; if (!udp && !trans) { if (!got_srvr_retrans) retransinfo = -1; if (!got_srvr_cwnd) cwndinfo = 0; } } if (!udp && trans) { if (retransinfo > 0) { total_retrans = 0; for ( stream_idx = 1; stream_idx <= nstream; stream_idx++ ) { total_retrans += nretrans[stream_idx]; } } if (cwndinfo) { total_snd_cwnd = 0; for ( stream_idx = 1; stream_idx <= nstream; stream_idx++ ) { total_snd_cwnd += cwnd[stream_idx]; } } } if (brief <= 0) { strcpy(fmt, "nuttcp%s%s: "); if (format & PARSE) strcat(fmt, P_PERF_FMT_OUT); else strcat(fmt, PERF_FMT_OUT); fprintf(stdout, fmt, trans?"-t":"-r", ident, (double)nbytes/(1024*1024), realt, (double)nbytes/realt/1024, (double)nbytes/realt/125000); if (clientserver && client && !trans && udp) { fprintf(stdout, "nuttcp-r%s:", ident); if (format & PARSE) strcpy(fmt, P_DROP_FMT); else strcpy(fmt, DROP_FMT); fprintf(stdout, fmt, (int64_t)(((srvr_MB - MB)*1024*1024) /buflen + 0.5), (uint64_t)((srvr_MB*1024*1024)/buflen + 0.5)); fractloss = ((srvr_MB != 0.0) ? 1 - MB/srvr_MB : 0.0); if (format & PARSE) strcpy(fmt, P_LOSS_FMT); else if ((fractloss != 0.0) && (fractloss < 0.001)) strcpy(fmt, LOSS_FMT5); else strcpy(fmt, LOSS_FMT); fprintf(stdout, fmt, fractloss * 100); fprintf(stdout, "\n"); } if (clientserver && udp && !trans && do_jitter && njitter) { strcpy(fmt, "nuttcp%s%s: "); if (format & PARSE) strcat(fmt, P_JITTER_FMT); else strcat(fmt, JITTER_FMT); fprintf(stdout, fmt, trans?"-t":"-r", ident, jitter_min, jitter_avg/njitter, jitter_max); fprintf(stdout, "\n"); } if (clientserver && !trans && do_owd && nowd) { strcpy(fmt, "nuttcp%s%s: "); if (format & PARSE) strcat(fmt, P_OWD_FMT); else strcat(fmt, OWD_FMT); fprintf(stdout, fmt, trans?"-t":"-r", ident, owd_min, owd_avg/nowd, owd_max); fprintf(stdout, "\n"); } if (verbose) { strcpy(fmt, "nuttcp%s%s: "); if (format & PARSE) strcat(fmt, "megabytes=%.4f cpu_seconds=%.2f KB_per_cpu_second=%.2f\n"); else strcat(fmt, "%.4f MB in %.2f CPU seconds = %.2f KB/cpu sec\n"); fprintf(stdout, fmt, trans?"-t":"-r", ident, (double)nbytes/(1024*1024), cput, (double)nbytes/cput/1024); } if (!udp && trans && (retransinfo > 0)) { fprintf(stdout, "nuttcp%s%s: ", trans ? "-t" : "-r", ident); if (format & PARSE) strcpy(fmt, P_RETRANS_FMT); else strcpy(fmt, RETRANS_FMT); fprintf(stdout, fmt, retransinfo == 1 ? "" : "host-", total_retrans); if ((nstream > 1) && (retransinfo == 1) && total_retrans) { if (format & PARSE) fprintf(stdout, P_RETRANS_FMT_STREAMS, nretrans[1]); else fprintf(stdout, " ( %d", nretrans[1]); for ( stream_idx = 2; stream_idx <= nstream; stream_idx++ ) { fprintf(stdout, "+%d", nretrans[stream_idx]); } if (!(format & PARSE)) fprintf(stdout, " )"); } #if defined(linux) if (cwndinfo) { if (format & PARSE) strcpy(fmt, P_CWND_FMT); else strcpy(fmt, CWND_FMT); fprintf(stdout, fmt, total_snd_cwnd); if ((nstream > 1) && (retransinfo == 1) && total_snd_cwnd) { if (format & PARSE) fprintf(stdout, P_CWND_FMT_STREAMS, cwnd[1]); else fprintf(stdout, " ( %d", cwnd[1]); for ( stream_idx = 2; stream_idx <= nstream; stream_idx++ ) { fprintf(stdout, "+%d", cwnd[stream_idx]); } if (!(format & PARSE)) fprintf(stdout, " )"); } } #endif fprintf(stdout, "\n"); } strcpy(fmt, "nuttcp%s%s: "); if (format & PARSE) strcat(fmt, "io_calls=%d msec_per_call=%.2f calls_per_sec=%.2f\n"); else strcat(fmt, "%d I/O calls, msec/call = %.2f, calls/sec = %.2f\n"); fprintf(stdout, fmt, trans?"-t":"-r", ident, numCalls, 1024.0 * realt/((double)numCalls), ((double)numCalls)/realt); strcpy(fmt, "nuttcp%s%s: "); if (format & PARSE) strcat(fmt, "stats=cpu %s\n"); else strcat(fmt, "%s\n"); fprintf(stdout, fmt, trans?"-t":"-r", ident, stats); } if (format & PARSE) strcpy(fmt, P_CPU_STATS_FMT_IN2); else strcpy(fmt, CPU_STATS_FMT_IN2); if (sscanf(stats, fmt, &cpu_util) != 6) { if (format & PARSE) strcpy(fmt, P_CPU_STATS_FMT_IN); else strcpy(fmt, CPU_STATS_FMT_IN); sscanf(stats, fmt, &cpu_util); } if (brief && clientserver && client) { if ((brief < 0) || interval) fprintf(stdout, "\n"); if (udp) { if (trans) { if (*ident) fprintf(stdout, "%s: ", ident + 1); if (format & PARSE) strcpy(fmt, P_PERF_FMT_BRIEF); else strcpy(fmt, PERF_FMT_BRIEF); fprintf(stdout, fmt, srvr_MB, srvr_realt, srvr_Mbps, cpu_util, srvr_cpu_util); if (!(format & NODROPS)) { if (format & PARSE) strcpy(fmt, P_DROP_FMT_BRIEF); else strcpy(fmt, DROP_FMT_BRIEF); fprintf(stdout, fmt, (int64_t)(((MB - srvr_MB) *1024*1024) /buflen + 0.5), (uint64_t)((MB*1024*1024) /buflen + 0.5)); } if (!(format & NOPERCENTLOSS)) { fractloss = ((MB != 0.0) ? 1 - srvr_MB/MB : 0.0); if (format & PARSE) strcpy(fmt, P_LOSS_FMT_BRIEF); else if ((fractloss != 0.0) && (fractloss < 0.001)) strcpy(fmt, LOSS_FMT_BRIEF5); else strcpy(fmt, LOSS_FMT_BRIEF); fprintf(stdout, fmt, fractloss * 100); } if (format & XMITSTATS) { if (format & PARSE) strcpy(fmt, P_PERF_FMT_BRIEF3); else strcpy(fmt, PERF_FMT_BRIEF3); fprintf(stdout, fmt, MB); } if ((do_jitter & JITTER_MIN) && njitter) { if (format & PARSE) strcpy(fmt, P_JITTER_MIN_FMT_BRIEF); else strcpy(fmt, JITTER_MIN_FMT_BRIEF); fprintf(stdout, fmt, jitter_min); } if ((do_jitter & JITTER_AVG) && njitter) { if (format & PARSE) strcpy(fmt, P_JITTER_AVG_FMT_BRIEF); else strcpy(fmt, JITTER_AVG_FMT_BRIEF); fprintf(stdout, fmt, jitter_avg/njitter); } if ((do_jitter & JITTER_MAX) && njitter) { if (format & PARSE) strcpy(fmt, P_JITTER_MAX_FMT_BRIEF); else strcpy(fmt, JITTER_MAX_FMT_BRIEF); fprintf(stdout, fmt, jitter_max); } if ((do_owd & OWD_MIN) && nowd) { if (format & PARSE) strcpy(fmt, P_OWD_MIN_FMT_BRIEF); else strcpy(fmt, OWD_MIN_FMT_BRIEF); fprintf(stdout, fmt, owd_min); } if ((do_owd & OWD_AVG) && nowd) { if (format & PARSE) strcpy(fmt, P_OWD_AVG_FMT_BRIEF); else strcpy(fmt, OWD_AVG_FMT_BRIEF); fprintf(stdout, fmt, owd_avg/nowd); } if ((do_owd & OWD_MAX) && nowd) { if (format & PARSE) strcpy(fmt, P_OWD_MAX_FMT_BRIEF); else strcpy(fmt, OWD_MAX_FMT_BRIEF); fprintf(stdout, fmt, owd_max); } } else { if (*ident) fprintf(stdout, "%s: ", ident + 1); if (format & PARSE) strcpy(fmt, P_PERF_FMT_BRIEF); else strcpy(fmt, PERF_FMT_BRIEF); fprintf(stdout, fmt, MB, realt, (double)nbytes/realt/125000, srvr_cpu_util, cpu_util); if (!(format & NODROPS)) { if (format & PARSE) strcpy(fmt, P_DROP_FMT_BRIEF); else strcpy(fmt, DROP_FMT_BRIEF); fprintf(stdout, fmt, (int64_t)(((srvr_MB - MB) *1024*1024) /buflen + 0.5), (uint64_t)((srvr_MB*1024*1024) /buflen + 0.5)); } if (!(format & NOPERCENTLOSS)) { fractloss = ((srvr_MB != 0.0) ? 1 - MB/srvr_MB : 0.0); if (format & PARSE) strcpy(fmt, P_LOSS_FMT_BRIEF); else if ((fractloss != 0.0) && (fractloss < 0.001)) strcpy(fmt, LOSS_FMT_BRIEF5); else strcpy(fmt, LOSS_FMT_BRIEF); fprintf(stdout, fmt, fractloss * 100); } if (format & XMITSTATS) { if (format & PARSE) strcpy(fmt, P_PERF_FMT_BRIEF3); else strcpy(fmt, PERF_FMT_BRIEF3); fprintf(stdout, fmt, srvr_MB); } if ((do_jitter & JITTER_MIN) && njitter) { if (format & PARSE) strcpy(fmt, P_JITTER_MIN_FMT_BRIEF); else strcpy(fmt, JITTER_MIN_FMT_BRIEF); fprintf(stdout, fmt, jitter_min); } if ((do_jitter & JITTER_AVG) && njitter) { if (format & PARSE) strcpy(fmt, P_JITTER_AVG_FMT_BRIEF); else strcpy(fmt, JITTER_AVG_FMT_BRIEF); fprintf(stdout, fmt, jitter_avg/njitter); } if ((do_jitter & JITTER_MAX) && njitter) { if (format & PARSE) strcpy(fmt, P_JITTER_MAX_FMT_BRIEF); else strcpy(fmt, JITTER_MAX_FMT_BRIEF); fprintf(stdout, fmt, jitter_max); } if ((do_owd & OWD_MIN) && nowd) { if (format & PARSE) strcpy(fmt, P_OWD_MIN_FMT_BRIEF); else strcpy(fmt, OWD_MIN_FMT_BRIEF); fprintf(stdout, fmt, owd_min); } if ((do_owd & OWD_AVG) && nowd) { if (format & PARSE) strcpy(fmt, P_OWD_AVG_FMT_BRIEF); else strcpy(fmt, OWD_AVG_FMT_BRIEF); fprintf(stdout, fmt, owd_avg/nowd); } if ((do_owd & OWD_MAX) && nowd) { if (format & PARSE) strcpy(fmt, P_OWD_MAX_FMT_BRIEF); else strcpy(fmt, OWD_MAX_FMT_BRIEF); fprintf(stdout, fmt, owd_max); } } fprintf(stdout, "\n"); } else if (trans) { if ((retransinfo > 0) && (!(format & NORETRANS))) { if (format & DEBUGRETRANS) { sretrans = get_retrans(-1, &tcpinf); fprintf(stdout, "report system retrans = %d\n", sretrans); } } if (*ident) fprintf(stdout, "%s: ", ident + 1); if (format & PARSE) strcpy(fmt, P_PERF_FMT_BRIEF); else strcpy(fmt, PERF_FMT_BRIEF); fprintf(stdout, fmt, srvr_MB, srvr_realt, srvr_Mbps, cpu_util, srvr_cpu_util); if ((nstream > 1) && (retransinfo == 1) && total_retrans && !(format & NORETRANS) && (brief & BRIEF_RETRANS_STREAMS)) { if (format & PARSE) { fprintf(stdout, P_RETRANS_FMT_BRIEF, "", total_retrans); fprintf(stdout, P_RETRANS_FMT_STREAMS, nretrans[1]); } else { fprintf(stdout, RETRANS_FMT_BRIEF_STR1, total_retrans, nretrans[1]); } for ( stream_idx = 2; stream_idx <= nstream; stream_idx++ ) { fprintf(stdout, "+%d", nretrans[stream_idx]); } if (!(format & PARSE)) fprintf(stdout, RETRANS_FMT_BRIEF_STR2); } else if ((retransinfo > 0) && (!(format & NORETRANS))) { if (format & PARSE) fprintf(stdout, P_RETRANS_FMT_BRIEF, retransinfo == 1 ? "" : "host-", total_retrans); else fprintf(stdout, RETRANS_FMT_BRIEF, total_retrans, retransinfo == 1 ? "" : "host-"); } #if defined(linux) if ((nstream > 1) && cwndinfo && (retransinfo == 1) && total_snd_cwnd && !(format & NOCWND) && (brief & BRIEF_CWND_STREAMS)) { if (format & PARSE) { fprintf(stdout, P_CWND_FMT_BRIEF, total_snd_cwnd); fprintf(stdout, P_CWND_FMT_STREAMS, cwnd[1]); } else { fprintf(stdout, CWND_FMT_BRIEF_STR1, total_snd_cwnd, cwnd[1]); } for ( stream_idx = 2; stream_idx <= nstream; stream_idx++ ) { fprintf(stdout, "+%d", cwnd[stream_idx]); } if (!(format & PARSE)) fprintf(stdout, CWND_FMT_BRIEF_STR2); } else if (cwndinfo && (!(format & NOCWND))) { if (format & PARSE) fprintf(stdout, P_CWND_FMT_BRIEF, total_snd_cwnd); else fprintf(stdout, CWND_FMT_BRIEF, total_snd_cwnd); } #endif if (rtt && (format & WANTRTT)) { if (format & PARSE) strcpy(fmt, P_RTT_FMT_BRIEF); else strcpy(fmt, RTT_FMT_BRIEF); fprintf(stdout, fmt, rtt); } if ((do_owd & OWD_MIN) && nowd) { if (format & PARSE) strcpy(fmt, P_OWD_MIN_FMT_BRIEF); else strcpy(fmt, OWD_MIN_FMT_BRIEF); fprintf(stdout, fmt, owd_min); } if ((do_owd & OWD_AVG) && nowd) { if (format & PARSE) strcpy(fmt, P_OWD_AVG_FMT_BRIEF); else strcpy(fmt, OWD_AVG_FMT_BRIEF); fprintf(stdout, fmt, owd_avg/nowd); } if ((do_owd & OWD_MAX) && nowd) { if (format & PARSE) strcpy(fmt, P_OWD_MAX_FMT_BRIEF); else strcpy(fmt, OWD_MAX_FMT_BRIEF); fprintf(stdout, fmt, owd_max); } fprintf(stdout, "\n"); } else { if (*ident) fprintf(stdout, "%s: ", ident + 1); if (format & PARSE) strcpy(fmt, P_PERF_FMT_BRIEF); else strcpy(fmt, PERF_FMT_BRIEF); fprintf(stdout, fmt, MB, realt, (double)nbytes/realt/125000, srvr_cpu_util, cpu_util); if ((nstream > 1) && (retransinfo == 1) && total_retrans && !(format & NORETRANS) && (brief & BRIEF_RETRANS_STREAMS) && (irvers >= 70101)) { if (format & PARSE) { fprintf(stdout, P_RETRANS_FMT_BRIEF, "", total_retrans); fprintf(stdout, P_RETRANS_FMT_STREAMS, nretrans[1]); } else { fprintf(stdout, RETRANS_FMT_BRIEF_STR1, total_retrans, nretrans[1]); } for ( stream_idx = 2; stream_idx <= nstream; stream_idx++ ) { fprintf(stdout, "+%d", nretrans[stream_idx]); } if (!(format & PARSE)) fprintf(stdout, RETRANS_FMT_BRIEF_STR2); } else if ((retransinfo > 0) && (!(format & NORETRANS))) { if (format & PARSE) fprintf(stdout, P_RETRANS_FMT_BRIEF, retransinfo == 1 ? "" : "host-", total_retrans); else fprintf(stdout, RETRANS_FMT_BRIEF, total_retrans, retransinfo == 1 ? "" : "host-"); } if ((nstream > 1) && cwndinfo && (retransinfo == 1) && total_snd_cwnd && !(format & NOCWND) && (brief & BRIEF_CWND_STREAMS) && (irvers >= 80001)) { if (format & PARSE) { fprintf(stdout, P_CWND_FMT_BRIEF, total_snd_cwnd); fprintf(stdout, P_CWND_FMT_STREAMS, cwnd[1]); } else { fprintf(stdout, CWND_FMT_BRIEF_STR1, total_snd_cwnd, cwnd[1]); } for ( stream_idx = 2; stream_idx <= nstream; stream_idx++ ) { fprintf(stdout, "+%d", cwnd[stream_idx]); } if (!(format & PARSE)) fprintf(stdout, CWND_FMT_BRIEF_STR2); } else if (cwndinfo && (!(format & NOCWND))) { if (format & PARSE) fprintf(stdout, P_CWND_FMT_BRIEF, total_snd_cwnd); else fprintf(stdout, CWND_FMT_BRIEF, total_snd_cwnd); } if (rtt && (format & WANTRTT)) { if (format & PARSE) strcpy(fmt, P_RTT_FMT_BRIEF); else strcpy(fmt, RTT_FMT_BRIEF); fprintf(stdout, fmt, rtt); } if ((do_owd & OWD_MIN) && nowd) { if (format & PARSE) strcpy(fmt, P_OWD_MIN_FMT_BRIEF); else strcpy(fmt, OWD_MIN_FMT_BRIEF); fprintf(stdout, fmt, owd_min); } if ((do_owd & OWD_AVG) && nowd) { if (format & PARSE) strcpy(fmt, P_OWD_AVG_FMT_BRIEF); else strcpy(fmt, OWD_AVG_FMT_BRIEF); fprintf(stdout, fmt, owd_avg/nowd); } if ((do_owd & OWD_MAX) && nowd) { if (format & PARSE) strcpy(fmt, P_OWD_MAX_FMT_BRIEF); else strcpy(fmt, OWD_MAX_FMT_BRIEF); fprintf(stdout, fmt, owd_max); } fprintf(stdout, "\n"); } } else { if (brief && !clientserver) { if (brief < 0) fprintf(stdout, "\n"); if (*ident) fprintf(stdout, "%s: ", ident + 1); fprintf(stdout, PERF_FMT_BRIEF2 "\n", MB, realt, (double)nbytes/realt/125000, cpu_util, trans?"TX":"RX"); } } cleanup: if (clientserver) { if (client) { itimer.it_value.tv_sec = SRVR_INFO_TIMEOUT; itimer.it_value.tv_usec = 0; setitimer(ITIMER_REAL, &itimer, 0); if (brief <= 0) fputs("\n", stdout); if (brief <= 0) { if (got_srvr_output) { fputs(srvrbuf, stdout); } } else { while (fgets(buf, mallocsize, stdin)) { setitimer(ITIMER_REAL, &itimer, 0); fputs(buf, stdout); } } itimer.it_value.tv_sec = 0; itimer.it_value.tv_usec = 0; setitimer(ITIMER_REAL, &itimer, 0); fflush(stdout); close(0); } else { fflush(stdout); close(1); if (!inetd) { dup(savestdout); close(savestdout); fflush(stderr); if (!nofork) { close(2); dup(1); } } } fclose(ctlconn); #ifdef DEBUG if (irate && (format & DEBUGIRATE)) if (debugout) { fprintf(debugout, "END nuttcp debug output\n"); fclose(debugout); } #endif if (!inetd) close(fd[0]); if (!udp && trans && (retransinfo > 0)) { if (format & DEBUGRETRANS) { sretrans = get_retrans(-1, &tcpinf); fprintf(stdout, "final system retrans = %d\n", sretrans); } } } if (clientserver && !client) { for ( stream_idx = 1; stream_idx <= nstream; stream_idx++ ) { fd[stream_idx] = -1; } if (multilink && ((trans && !reverse) || (!trans && reverse)) && !udp) { for ( stream_idx = 2; stream_idx <= nstream; stream_idx++ ) { res[stream_idx] = NULL; } } itimer.it_value.tv_sec = 0; itimer.it_value.tv_usec = 0; itimer.it_interval.tv_sec = 0; itimer.it_interval.tv_usec = 0; setitimer(ITIMER_REAL, &itimer, 0); signal(SIGALRM, SIG_DFL); bzero((char *)&frominet, sizeof(frominet)); bzero((char *)&clientaddr, sizeof(clientaddr)); #ifdef AF_INET6 bzero((char *)&clientaddr6, sizeof(clientaddr6)); clientscope6 = 0; #endif cput = 0.000001; realt = 0.000001; nbytes = 0; ntbytes = 0; ntbytesc = 0; chk_nbytes = 0; numCalls = 0; /* Don't re-initialize buflen since it's used to */ /* determine if we need to change the buffer memory */ /* allocation for the next client data stream request */ /* buflen = 64 * 1024; */ /* if (udp) buflen = DEFAULTUDPBUFLEN; */ nbuf = 0; sendwin = origsendwin; rcvwin = origrcvwin; b_flag = 1; rate = MAXRATE; maxburst = 1; nburst = 1; irate = 0; iratesss = 0; irate_cum_nsec = 0.0; timeout = 0.0; interval = 0.0; chk_interval = 0.0; chk_idle_data = 0.0; datamss = 0; tos = 0; nodelay = 0; do_poll = 0; pbytes = 0; ptbytes = 0; ident[0] = '\0'; intr = 0; abortconn = 0; ipad_stride.ip32 = 0; port = 5101; srcport = 0; trans = 0; braindead = 0; udp = 0; udplossinfo = 0; do_jitter = 0; do_owd = 0; retransinfo = 0; init_pkt_cwnd = 0; sss_pkt_cwnd = 0; cwndinfo = 0; force_retrans = 0; rtt = 0.0; which_rt = 1; pretrans = 0; sretrans = 0; got_srvr_output = 0; reading_srvr_info = 0; reverse = 0; format = 0; traceroute = 0; multicast = 0; mc_addr = NULL; ssm = -1; skip_data = 0; host3 = NULL; thirdparty = 0; ctlport3 = 0; nbuf_bytes = 0; rate_pps = 0; brief = 0; done = 0; got_begin = 0; two_bod = 0; handle_urg = 0; for ( stream_idx = 0; stream_idx <= nstream; stream_idx++ ) { if (res[stream_idx]) { freeaddrinfo(res[stream_idx]); res[stream_idx] = NULL; } nretrans[stream_idx] = 0; iretrans[stream_idx] = 0; cwnd[stream_idx] = 0; } nstream = 1; multilink = 0; if (!oneshot) goto doit; exit(0); } if (multilink) { for ( stream_idx = 2; stream_idx <= nstream; stream_idx++ ) { res[stream_idx] = NULL; } } for ( stream_idx = 0; stream_idx <= nstream; stream_idx++ ) { if (res[stream_idx]) { freeaddrinfo(res[stream_idx]); res[stream_idx] = NULL; } } exit(0); usage: fprintf(stdout, Usage); exit(1); } static void err( char *s ) { long flags, saveflags; fprintf(stderr, "nuttcp%s%s: v%d.%d.%d%s: Error: ", trans?"-t":"-r", ident, vers_major, vers_minor, vers_delta, beta ? BETA_STR : ""); perror(s); fprintf(stderr, "errno=%d\n", errno); fflush(stderr); if ((stream_idx > 0) && !done && clientserver && !client && !trans && handle_urg) { /* send 'A' for ABORT as urgent TCP data * on control connection (don't block) */ saveflags = fcntl(fd[0], F_GETFL, 0); if (saveflags != -1) { flags = saveflags | O_NONBLOCK; fcntl(fd[0], F_SETFL, flags); } send(fd[0], "A", 1, MSG_OOB); if (saveflags != -1) { flags = saveflags; fcntl(fd[0], F_SETFL, flags); } } exit(1); } static void mes( char *s ) { fprintf(stdout, "nuttcp%s%s: v%d.%d.%d%s: %s\n", trans?"-t":"-r", ident, vers_major, vers_minor, vers_delta, beta ? BETA_STR : "", s); } static void errmes( char *s ) { fprintf(stderr, "nuttcp%s%s: v%d.%d.%d%s: Error: ", trans?"-t":"-r", ident, vers_major, vers_minor, vers_delta, beta ? BETA_STR : ""); perror(s); fprintf(stderr, "errno=%d\n", errno); fflush(stderr); } void pattern( register char *cp, register int cnt ) { register char c; c = 0; while (cnt-- > 0) { while (!isprint((c&0x7F))) c++; *cp++ = (c++&0x7F); } } void get_timeofday( struct timeval *tv, struct timezone *tz ) { #ifdef HAVE_CLOCK_GETTIME struct timespec tod; clockid_t clk_id; #if defined(_POSIX_MONOTONIC_CLOCK) || defined(__CYGWIN__) if (do_owd) { clk_id = CLOCK_REALTIME; } else { clk_id = CLOCK_MONOTONIC; } #else /* !(defined(_POSIX_MONOTONIC_CLOCK) || defined(__CYGWIN__)) */ clk_id = CLOCK_REALTIME; #endif /* defined(_POSIX_MONOTONIC_CLOCK) || defined(__CYGWIN__) */ clock_gettime( clk_id, &tod ); tv->tv_sec = tod.tv_sec; tv->tv_usec = tod.tv_nsec / 1000; #else /* !HAVE_CLOCK_GETTIME */ gettimeofday( tv, tz ); #endif /* HAVE_CLOCK_GETTIME */ return; } /* * P R E P _ T I M E R */ void prep_timer() { get_timeofday(&time0, (struct timezone *)0); timep.tv_sec = time0.tv_sec; timep.tv_usec = time0.tv_usec; timepk.tv_sec = time0.tv_sec; timepk.tv_usec = time0.tv_usec; getrusage(RUSAGE_SELF, &ru0); } /* * R E A D _ T I M E R * */ double read_timer( char *str, int len ) { struct timeval timedol; struct rusage ru1; struct timeval td; struct timeval tend, tstart; char line[132]; getrusage(RUSAGE_SELF, &ru1); get_timeofday(&timedol, (struct timezone *)0); prusage(&ru0, &ru1, &timedol, &time0, line); (void)strncpy( str, line, len ); /* Get real time */ tvsub( &td, &timedol, &time0 ); realt = td.tv_sec + ((double)td.tv_usec) / 1000000; /* Get CPU time (user+sys) */ tvadd( &tend, &ru1.ru_utime, &ru1.ru_stime ); tvadd( &tstart, &ru0.ru_utime, &ru0.ru_stime ); tvsub( &td, &tend, &tstart ); cput = td.tv_sec + ((double)td.tv_usec) / 1000000; if (cput < 0.00001) cput = 0.00001; return( cput ); } static void prusage( register struct rusage *r0, register struct rusage *r1, struct timeval *e, struct timeval *b, char *outp ) { struct timeval tdiff; register time_t t; register char *cp; register int i; int ms; t = (r1->ru_utime.tv_sec-r0->ru_utime.tv_sec)*100+ (r1->ru_utime.tv_usec-r0->ru_utime.tv_usec)/10000+ (r1->ru_stime.tv_sec-r0->ru_stime.tv_sec)*100+ (r1->ru_stime.tv_usec-r0->ru_stime.tv_usec)/10000; ms = (e->tv_sec-b->tv_sec)*100 + (e->tv_usec-b->tv_usec)/10000; #define END(x) { while (*x) x++; } if (format & PARSE) cp = "user=%U system=%S elapsed=%E cpu=%P memory=%Xi+%Dd-%Mmaxrss io=%F+%Rpf swaps=%Ccsw"; else cp = "%Uuser %Ssys %Ereal %P %Xi+%Dd %Mmaxrss %F+%Rpf %Ccsw"; for ( ; *cp; cp++ ) { if (*cp != '%') *outp++ = *cp; else if (cp[1]) switch(*++cp) { case 'U': tvsub(&tdiff, &r1->ru_utime, &r0->ru_utime); sprintf(outp, "%ld.%01ld", (long)tdiff.tv_sec, (long)tdiff.tv_usec/100000); END(outp); break; case 'S': tvsub(&tdiff, &r1->ru_stime, &r0->ru_stime); sprintf(outp, "%ld.%01ld", (long)tdiff.tv_sec, (long)tdiff.tv_usec/100000); END(outp); break; case 'E': psecs(ms / 100, outp); END(outp); break; case 'P': sprintf(outp, "%d%%", (int) (t*100 / ((ms ? ms : 1)))); END(outp); break; case 'W': i = r1->ru_nswap - r0->ru_nswap; sprintf(outp, "%d", i); END(outp); break; case 'X': sprintf(outp, "%ld", t == 0 ? 0 : (r1->ru_ixrss-r0->ru_ixrss)/t); END(outp); break; case 'D': sprintf(outp, "%ld", t == 0 ? 0 : (r1->ru_idrss+r1->ru_isrss-(r0->ru_idrss+r0->ru_isrss))/t); END(outp); break; case 'K': sprintf(outp, "%ld", t == 0 ? 0 : ((r1->ru_ixrss+r1->ru_isrss+r1->ru_idrss) - (r0->ru_ixrss+r0->ru_idrss+r0->ru_isrss))/t); END(outp); break; case 'M': sprintf(outp, "%ld", r1->ru_maxrss/2); END(outp); break; case 'F': sprintf(outp, "%ld", r1->ru_majflt-r0->ru_majflt); END(outp); break; case 'R': sprintf(outp, "%ld", r1->ru_minflt-r0->ru_minflt); END(outp); break; case 'I': sprintf(outp, "%ld", r1->ru_inblock-r0->ru_inblock); END(outp); break; case 'O': sprintf(outp, "%ld", r1->ru_oublock-r0->ru_oublock); END(outp); break; case 'C': sprintf(outp, "%ld+%ld", r1->ru_nvcsw-r0->ru_nvcsw, r1->ru_nivcsw-r0->ru_nivcsw); END(outp); break; } } *outp = '\0'; } static void tvadd( struct timeval *tsum, struct timeval *t0, struct timeval *t1 ) { tsum->tv_sec = t0->tv_sec + t1->tv_sec; tsum->tv_usec = t0->tv_usec + t1->tv_usec; if (tsum->tv_usec > 1000000) tsum->tv_sec++, tsum->tv_usec -= 1000000; } static void tvsub( struct timeval *tdiff, struct timeval *t1, struct timeval *t0 ) { tdiff->tv_sec = t1->tv_sec - t0->tv_sec; tdiff->tv_usec = t1->tv_usec - t0->tv_usec; if (tdiff->tv_usec < 0) tdiff->tv_sec--, tdiff->tv_usec += 1000000; } static void psecs( long l, register char *cp ) { register int i; i = l / 3600; if (i) { sprintf(cp, "%d:", i); END(cp); i = l % 3600; sprintf(cp, "%d%d", (i/60) / 10, (i/60) % 10); END(cp); } else { i = l; sprintf(cp, "%d", i / 60); END(cp); } i %= 60; *cp++ = ':'; sprintf(cp, "%d%d", i / 10, i % 10); } /* * N R E A D */ int Nread( int fd, char *buf, int count ) { struct sockaddr_storage from; socklen_t len = sizeof(from); struct timeval timed; /* time delta */ register int cnt; if (udp) { cnt = recvfrom( fd, buf, count, 0, (struct sockaddr *)&from, &len ); numCalls++; } else { if (b_flag) cnt = mread( fd, buf, count ); /* fill buf */ else { cnt = read( fd, buf, count ); numCalls++; } } if (do_owd && (cnt > 4)) { uint32_t secs, usecs; /* get transmitter timestamp */ bcopy(buf + 8, &secs, 4); bcopy(buf + 12, &usecs, 4); timetx.tv_sec = ntohl(secs); timetx.tv_usec = ntohl(usecs); get_timeofday(&timerx, (struct timezone *)0); tvsub( &timed, &timerx, &timetx ); owd = timed.tv_sec*1000 + ((double)timed.tv_usec)/1000; nowd++; if (owd < owd_min) owd_min = owd; if (owd > owd_max) owd_max = owd; owd_avg += owd; nowdi++; if (owd < owd_mini) owd_mini = owd; if (owd > owd_maxi) owd_maxi = owd; owd_avgi += owd; } return(cnt); } /* * N W R I T E */ int Nwrite( int fd, char *buf, int count ) { struct timeval timedol; struct timeval td; register int cnt = 0; double deltat, delta0; double pktdelay = 0; int pktdelay_sec, pktdelay_usec, sssdelay; if (irate) { sssdelay = 0; /* Get real time */ get_timeofday(&timedol, (struct timezone *)0); tvsub( &td, &timedol, &timepk ); deltat = td.tv_sec + ((double)td.tv_usec) / 1000000; if (deltat >= (1 + maxburst)*pkt_time) { timepk.tv_sec = timedol.tv_sec; timepk.tv_usec = timedol.tv_usec; irate_cum_nsec = 0; deltat = 0.0; nburst = 1; } if (nburst++ >= maxburst) { if (iratesss && cwndinfo) { /* note iratesss only supported for 1 stream */ tvsub( &td, &timedol, &time0 ); delta0 = td.tv_sec + ((double)td.tv_usec) / 1000000; if (delta0 > which_rt*rtt/1000) { which_rt++; sss_pkt_cwnd = (cwnd[1] + 1)*1024/datamss; sss_pkt_cwnd *= 2; } pktdelay = rtt/1000/sss_pkt_cwnd*count/datamss; if (pktdelay < maxburst*(double)count/rate/125) { pktdelay = maxburst*(double)count/rate/125; iratesss = 0; } else { sssdelay = 1; } } else { pktdelay = maxburst*(double)count/rate/125; } while ((pktdelay > deltat) && !intr) { /* Get real time */ get_timeofday(&timedol, (struct timezone *)0); tvsub( &td, &timedol, &timepk ); deltat = td.tv_sec + ((double)td.tv_usec) / 1000000; } } if (nburst > maxburst) { if (sssdelay) { pktdelay_sec = pktdelay; pktdelay_usec = (pktdelay - pktdelay_sec) *1000000; timepk.tv_usec += pktdelay_usec; if (timepk.tv_usec >= 1000000) { timepk.tv_usec -= 1000000; timepk.tv_sec += 1; } timepk.tv_sec += pktdelay_sec; } else { irate_cum_nsec += maxburst*irate_pk_nsec; while (irate_cum_nsec >= 1000.0) { irate_cum_nsec -= 1000.0; timepk.tv_usec++; } timepk.tv_usec += maxburst*irate_pk_usec; while (timepk.tv_usec >= 1000000) { timepk.tv_usec -= 1000000; timepk.tv_sec++; } } nburst = 1; } if (intr && (!udp || (count != 4))) return(0); } else { while ((double)nbytes/realt/125 > rate) { /* Get real time */ get_timeofday(&timedol, (struct timezone *)0); tvsub( &td, &timedol, &time0 ); realt = td.tv_sec + ((double)td.tv_usec) / 1000000; if (realt <= 0.0) realt = 0.000001; } } if (do_owd && (count > 4)) { uint32_t secs, usecs; /* record transmitter timestamp in packet */ get_timeofday(&timedol, (struct timezone *)0); secs = htonl(timedol.tv_sec); usecs = htonl(timedol.tv_usec); bcopy(&secs, buf + 8, 4); bcopy(&usecs, buf + 12, 4); } if (udp) { again: if (af == AF_INET) { cnt = sendto( fd, buf, count, 0, (struct sockaddr *)&sinhim[stream_idx + 1], sizeof(sinhim[stream_idx + 1]) ); } #ifdef AF_INET6 else if (af == AF_INET6) { cnt = sendto( fd, buf, count, 0, (struct sockaddr *)&sinhim6[stream_idx + 1], sizeof(sinhim6[stream_idx + 1]) ); } #endif else { err("unsupported AF"); } numCalls++; if (cnt<0 && errno == ENOBUFS) { delay(18000); errno = 0; goto again; } } else { #if defined(linux) && defined(DEBUG) if (irate && (format & DEBUGIRATE) && debugout) { struct timeval timewr; double pktwrtime; static int cnt = 0; cnt++; if (cnt == 1) { fprintf(debugout, "count = %d, datamss = %d, " "pktxmit = %.8f, " "rtt = %.6f, maxburst = %d\n", count, datamss, (double)count/rate/125, rtt/1000, maxburst); } get_timeofday(&timewr, (struct timezone *)0); tvsub( &td, &timewr, &time0 ); pktwrtime = td.tv_sec + (double)td.tv_usec/1000000; fprintf(debugout, "pktwrtime = %.6f, pktdelay = %.6f, " "sss_pkt_cwnd = %d, " "cur_pkt_cwnd = %d, iratesss = %d, " "which_rt = %d\n", pktwrtime, pktdelay, sss_pkt_cwnd, (cwnd[1] + 1)*1024/datamss, iratesss, which_rt); } #endif cnt = write( fd, buf, count ); numCalls++; } return(cnt); } int delay( int us ) { struct timeval tv; tv.tv_sec = 0; tv.tv_usec = us; (void)select( 1, (fd_set *)0, (fd_set *)0, (fd_set *)0, &tv ); return(1); } /* * M R E A D * * This function performs the function of a read(II) but will * call read(II) multiple times in order to get the requested * number of characters. This can be necessary because * network connections don't deliver data with the same * grouping as it is written with. Written by Robert S. Miles, BRL. */ int mread( int fd, register char *bufp, unsigned n ) { register unsigned count = 0; register int nread; do { nread = read(fd, bufp, n-count); numCalls++; if (nread < 0) { if (errno != EINTR) perror("nuttcp_mread"); return(-1); } if (nread == 0) return((int)count); count += (unsigned)nread; bufp += nread; } while (count < n); return((int)count); } /* * M W R I T E * * This function performs the function of a write(II) but will * call write(II) multiple times in order to put the requested * number of characters. This can be necessary because * piped connections may not be able to deliver data with * the full requested count. */ int mwrite( int fd, register char *bufp, unsigned n, int last_write ) { register unsigned count = 0; register int nwrite; #if defined(linux) long flags; if (directio && last_write) { flags = fcntl(savestdout, F_GETFL, 0); if (flags < 0) errmes("fcntl get O_DIRECT"); else { flags &= ~O_DIRECT; if (fcntl(savestdout, F_SETFL, flags) < 0) errmes("fcntl set O_DIRECT"); } } #endif do { nwrite = write(fd, bufp, n-count); numCalls++; if (nwrite < 0) { if (errno != EINTR) perror("nuttcp_mwrite"); return(-1); } count += (unsigned)nwrite; bufp += nwrite; } while (count < n); return((int)count); } /* * G E T O P T V A L P * * This function returns a character pointer to the option value * pointed at by argv and sets skiparg to 1 if the option and its * value were passed as separate arguments (otherwise it sets * skiparg to 0). index is the position within argv where the * option value resides if the option was specified as a single * argument. reqval indicates whether or not the option requires * a value */ char * getoptvalp( char **argv, int index, int reqval, int *skiparg ) { struct sockaddr_storage dummy; char **nextarg; *skiparg = 0; nextarg = argv + 1; /* if there is a value in the current arg return it */ if (argv[0][index]) return(&argv[0][index]); /* if there isn't a next arg return a pointer to the current arg value (which will be an empty string) */ if (*nextarg == NULL) return(&argv[0][index]); /* if the next arg is another option, and a value isn't * required, return a pointer to the current arg value * (which will be an empty string) */ if ((**nextarg == '-') && !reqval) return(&argv[0][index]); /* if there is an arg after the next arg and it is another option, return the next arg as the option value */ if (*(nextarg + 1) && (**(nextarg + 1) == '-')) { *skiparg = 1; return(*nextarg); } /* if the option requires a value, return the next arg as the option value */ if (reqval) { *skiparg = 1; return(*nextarg); } /* if the next arg is an Ipv4 address, return a pointer to the current arg value (which will be an empty string) */ if (inet_pton(AF_INET, *nextarg, &dummy) > 0) return(&argv[0][index]); #ifdef AF_INET6 /* if the next arg is an Ipv6 address, return a pointer to the current arg value (which will be an empty string) */ if (inet_pton(AF_INET6, *nextarg, &dummy) > 0) return(&argv[0][index]); #endif /* if the next arg begins with an alphabetic character, assume it is a hostname and thus return a pointer to the current arg value (which will be an empty string). note all current options which don't require a value have numeric values (start with a digit) */ if (isalpha((int)(**nextarg))) return(&argv[0][index]); /* assume the next arg is the option value */ *skiparg = 1; return(*nextarg); } #define PROC_SNMP "/proc/net/snmp" #define PROC_BUF_LEN 256 #define PROC_BUF_LEN2 128 #define NETSTAT "netstat" #if defined(linux) #define RETRANS "segments retransmited" #define NETSTAT_DIR "/bin/" #define NRETRANS_BEFORE #elif defined(__FreeBSD__) #define RETRANS "retransmitted" #define NETSTAT_DIR "/usr/bin/" #define NRETRANS_BEFORE #elif defined(__APPLE__) && defined(__MACH__) #define RETRANS "retransmitted" #define NETSTAT_DIR "/usr/sbin/" #define NRETRANS_BEFORE #elif defined(sparc) #define RETRANS "tcpRetransSegs" #define NETSTAT_DIR "/usr/bin/" #elif defined(sgi) #define RETRANS "retransmitted" #define NETSTAT_DIR "/usr/etc/" #define NRETRANS_BEFORE #elif defined(__CYGWIN__) || defined(_WIN32) #define RETRANS "Segments Retransmitted" #define NETSTAT_DIR "" #else #define RETRANS "retransmitted" #define NETSTAT_DIR "/usr/bin/" #define NRETRANS_BEFORE #endif char proc_buf[PROC_BUF_LEN]; char proc_buf2[PROC_BUF_LEN2]; int get_retrans( int sockfd, struct STRUCT_TCPINFO *tcpinfo ) { FILE *proc_snmp; char *cp, *cp2; int num_retrans; int pipefd[2]; int pidstat; pid_t pid = 0; pid_t wait_pid; if (retransinfo < 0) return(0); #if defined(linux) && defined(TCPI_OPT_TIMESTAMPS) if ((retransinfo <= 1) && (sockfd >= 0)) { optlen = sizeof(*tcpinfo); if (getsockopt(sockfd, SOL_TCP, TCP_INFO, (void *)tcpinfo, &optlen) == 0) { if (optlen >= SIZEOF_TCP_INFO_RETRANS) { retransinfo = 1; cwndinfo = 1; b_flag = 1; return(tcpinfo->tcpi_total_retrans); } } if (retransinfo == 1) { retransinfo = -1; cwndinfo = 0; return(0); } retransinfo = 2; cwndinfo = 0; } #else retransinfo = 2; cwndinfo = 0; #endif if ((retransinfo == 3) || (!(proc_snmp = fopen(PROC_SNMP, "r")))) { retransinfo = 3; cwndinfo = 0; if (pipe(pipefd) != 0) { retransinfo = -1; cwndinfo = 0; return(0); } if ((pid = fork()) == (pid_t)-1) { perror("can't fork"); close(pipefd[0]); close(pipefd[1]); retransinfo = -1; cwndinfo = 0; return(0); } if (pid == 0) { signal(SIGINT, SIG_DFL); close(1); close(2); dup(pipefd[1]); dup(pipefd[1]); close(pipefd[0]); close(pipefd[1]); execl(NETSTAT_DIR NETSTAT, NETSTAT, "-s", NULL); perror("execl failed"); fprintf(stderr, "failed to execute %s%s -s\n", NETSTAT_DIR, NETSTAT); fflush(stdout); fflush(stderr); exit(0); } close(pipefd[1]); if (!(proc_snmp = fdopen(pipefd[0], "r"))) { close(pipefd[0]); retransinfo = -1; cwndinfo = 0; return(0); } } errno = 0; num_retrans = -1; while (fgets(proc_buf, sizeof(proc_buf), proc_snmp)) { if (retransinfo == 2) { if (strncmp(proc_buf, "Tcp:", 4) != 0) continue; if ((!fgets(proc_buf2, sizeof(proc_buf2), proc_snmp)) || (strncmp(proc_buf2, "Tcp:", 4) != 0)) break; cp = proc_buf; cp2 = proc_buf2; while ((cp = strchr(cp, ' '))) { while (*++cp == ' ') ; if (!(*cp)) goto close; if (!(cp2 = strchr(cp2, ' '))) goto close; while (*++cp2 == ' ') ; if (!(*cp2)) goto close; if (strncmp(cp, "RetransSegs", 11) == 0) { if (!isdigit((int)(*cp2))) goto close; num_retrans = atoi(cp2); goto close; } else continue; } } else { if ((cp = strstr(proc_buf, RETRANS))) { #ifdef NRETRANS_BEFORE num_retrans = atoi(proc_buf); #else cp2 = strchr(cp, '='); cp2++; num_retrans = atoi(cp2); #endif break; } } } close: fclose(proc_snmp); if (retransinfo == 3) { while ((wait_pid = wait(&pidstat)) != pid) { if (wait_pid == (pid_t)-1) { if (errno == ECHILD) break; err("wait failed"); } } } if (num_retrans < 0) { retransinfo = -1; cwndinfo = 0; return(0); } return(num_retrans); } #if defined(linux) && defined(TCPI_OPT_TIMESTAMPS) void print_tcpinfo() { fprintf(stdout, "state = %d, ca_state = %d, retransmits = %d, " "unacked = %d, sacked = %d\n", tcpinf.tcpinfo_state, tcpinf.tcpinfo_ca_state, tcpinf.tcpinfo_retransmits, tcpinf.tcpinfo_unacked, tcpinf.tcpinfo_sacked); fprintf(stdout, " lost = %d, retrans = %d, fackets = %d, " "rtt = %d, rttvar = %d\n", tcpinf.tcpinfo_lost, tcpinf.tcpinfo_retrans, tcpinf.tcpinfo_fackets, tcpinf.tcpinfo_rtt, tcpinf.tcpinfo_rttvar); fprintf(stdout, " snd_ssthresh = %d, snd_cwnd = %d, " "total_retrans = %d\n", tcpinf.tcpinfo_snd_ssthresh, tcpinf.tcpinfo_snd_cwnd, tcpinf.tcpi_total_retrans); return; } #endif