vmtp/legal/nuttcp/nuttcp-8.1.4.c
Yichen Wang f2449844b3 Update nuttcp and iperf to latest
Change-Id: I9e06e759f7ccd82b682b69aa0b631fdb607f324d
2017-08-19 17:27:27 -07:00

10254 lines
279 KiB
C

/*
* N U T T C P . C v8.1.4
*
* Copyright(c) 2000 - 2016 Bill Fink. All rights reserved.
* Copyright(c) 2003 - 2016 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.4, Rob Scott, 1-Nov-16
* Fix to allow compile with -Werror=format-security as noted by Dhiru Kholia in fedora bug 1037224
* Fix missing brackets caught by -Wmisleading-indentation
* Add code to print out address family of connect/accept
* Add code to translate v4mapped addresses
* 8.1.3, Bill Fink, 9-Sep-16
* Fix to allow direct I/O on block devices
* Fix parsing of "-R i[s]##[/##]" (blank between 'R' and 'i')
* Updated Copyright notice for new year
* 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 <tab><spaces><tab>, <tab>$, & <spaces>$
* Whitespace cleanup: convert 8 spaces to <tab> 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 <control-C> 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 <control-C> 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 <stdio.h>
#include <signal.h>
#include <ctype.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/time.h> /* struct timeval */
#include <stdlib.h>
#ifndef _WIN32
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/resource.h>
#else
#include "win32nuttcp.h" /* from win32 */
#endif /* _WIN32 */
#include <limits.h>
#include <string.h>
#include <fcntl.h>
#if defined(linux)
#include <sys/stat.h>
#include <sys/sendfile.h>
#include <sys/ioctl.h>
#include <linux/sockios.h>
#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 <unistd.h>
#include <sys/wait.h>
#include <strings.h>
#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 <time.h> /* 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 <sys/poll.h>
#else
#include "fakepoll.h" /* from missing */
#endif
#ifdef HAVE_SETAFFINITY
#include <sched.h>
#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
/*
* The following macro is from openssh defines.h by Tatu Ylonen and marked "can be used freely for any purpose"
*/
#if !defined(IN6_IS_ADDR_V4MAPPED)
#define IN6_IS_ADDR_V4MAPPED(a) \
((*(const __uint32_t *)(const void *)(&(a)->s6_addr[0]) == 0) && \
(*(const __uint32_t *)(const void *)(&(a)->s6_addr[4]) == 0) && \
(*(const __uint32_t *)(const void *)(&(a)->s6_addr[8]) == \
ntohl(0x0000ffff)))
#endif /* !defined(IN6_IS_ADDR_V4MAPPED) */
#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 = 4;
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] [<in]\n\
|(receiver): nuttcp -r [-options] [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 <value|minimum/default/maximum> (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 <value|minimum/default/maximum> (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;
cp1 = getoptvalp(argv, 2, reqval, &skiparg);
if (*cp1 == 'i') {
cp1++;
if (*cp1 == 's') {
cp1++;
#if defined(linux)
iratesss = 1;
#else
fprintf(stderr, "smoothed slow start not supported for non-Linux\n");
fflush(stderr);
#endif
}
sscanf(cp1, "%lf", &rate_opt);
irate = 1;
}
else if (*cp1 == 'a') {
cp1++;
sscanf(cp1, "%lf", &rate_opt);
irate = 0;
}
else if (*cp1 == 'u') {
cp1++;
rate_opt = 0.0;
irate = 0;
}
else {
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 separate 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) {
if (!IN6_IS_ADDR_V4MAPPED(&sinhim6[stream_idx].sin6_addr)) {
inet_ntop(af, sinhim6[stream_idx].sin6_addr.s6_addr, tmphost, sizeof(tmphost));
}
else {
af = AF_INET;
bcopy(((char *)&sinhim6[stream_idx].sin6_addr) + 12,
(char *)&sinhim[stream_idx].sin_addr,
sizeof(struct in_addr));
sinhim[stream_idx].sin_family = AF_INET;
// port gets put in both structs, no translate or copy needed
inet_ntop(af, &sinhim[stream_idx].sin_addr.s_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 with",
trans?"-t":"-r", ident,
tmphost);
if (trans && datamss) {
fprintf(stdout, " mss=%d",
datamss);
if (rtt)
fprintf(stdout, ",");
}
if (rtt)
fprintf(stdout, RTT_FMT, rtt);
}
if (af == AF_INET) {
fprintf(stdout, " af=inet");
}
#ifdef AF_INET6
else if (af == AF_INET6) {
fprintf(stdout, " af=inet6");
}
#endif
else {
fprintf(stdout, " af=%d", af);
}
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 with",
trans?"-t":"-r", ident,
tmphost);
if (trans && datamss) {
fprintf(stdout, " mss=%d,",
datamss);
}
}
if (af == AF_INET) {
fprintf(stdout, " af=inet");
}
#ifdef AF_INET6
else if (af == AF_INET6) {
fprintf(stdout, " af=inet6");
}
#endif
else {
fprintf(stdout, " af=%d", af);
}
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 peer6;
struct sockaddr_in peer4;
socklen_t peerlen = sizeof(peer6);
if (getpeername(fd[stream_idx],
(struct sockaddr *)&peer6,
&peerlen) < 0) {
err("getpeername");
}
if ((stream_idx == nstream) && (brief <= 0)) {
char tmphost[ADDRSTRLEN] = "\0";
if (!IN6_IS_ADDR_V4MAPPED(&peer6.sin6_addr)) {
inet_ntop(af, peer6.sin6_addr.s6_addr, tmphost, sizeof(tmphost));
}
else {
af = AF_INET;
bcopy(((char *)&peer6.sin6_addr.s6_addr) + 12,
(char *)&peer4.sin_addr.s_addr,
sizeof(struct in_addr));
peer4.sin_family = AF_INET;
peer4.sin_port = peer6.sin6_port;
inet_ntop(af, &peer4.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 with",
trans?"-t":"-r", ident,
tmphost);
if (trans && datamss) {
fprintf(stdout, " mss=%d,",
datamss);
}
}
if (af == AF_INET) {
fprintf(stdout, " af=inet");
}
#ifdef AF_INET6
else if (af == AF_INET6) {
fprintf(stdout, " af=inet6");
}
#endif
else {
fprintf(stdout, " af=%d", af);
}
fprintf(stdout, "\n");
}
if (stream_idx == 0) {
if (af == AF_INET6) { // we didnt fix a mapped v4 ip
clientaddr6 = peer6.sin6_addr;
clientscope6 = peer6.sin6_scope_id;
client_ipaddr.ss.ss_family = AF_INET6;
client_ipaddr.sin6.sin6_addr = clientaddr6;
}
else { // mapped so af is AF_INET
clientaddr = peer4.sin_addr;
client_ipaddr.ss.ss_family = AF_INET;
client_ipaddr.sin.sin_addr = clientaddr;
}
}
}
#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) &&
!S_ISBLK(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) &&
!S_ISBLK(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