f2449844b3
Change-Id: I9e06e759f7ccd82b682b69aa0b631fdb607f324d
10254 lines
279 KiB
C
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
|
|
|