diff --git a/README.rst b/README.rst
index 59abd49..aa7e2e7 100644
--- a/README.rst
+++ b/README.rst
@@ -127,17 +127,88 @@ VMTP only measures performance for single-flows at the socket/TCP/UDP level (in
 
 It is not designed to measure driver level data path performance from inside a VM (such as bypassing the kernel TCP stack and write directly to virtio), there are better tools that can address this type of mesurement.
 
+VMTP ships with pre-built binaries that will run on most x86_64 Linux VMs (which is the vast majority of copute nodes) - see Licensing. Running VMTP on compute nodes that have a different CPU architecture will require rebuilding these binaries for the proper target.
 
 Licensing
 ---------
 
-VMTP is licensed under Apache License 2.0 and comes packaged with the following tools for convenience:
+VMTP is licensed under Apache License 2.0 and comes packaged with the following Linux x86_64 binaries for convenience:
 
-* iperf: BSD License (https://iperf.fr/license.html, source code: https://iperf.fr)
-* nuttcp: GPL v2 License (http://nuttcp.net/nuttcp/beta/LICENSE, source code: http://nuttcp.net/nuttcp/beta/nuttcp-7.3.2.c)
+* iperf 2.0.5: BSD License (https://iperf.fr/license.html, built from source code: https://sourceforge.net/projects/iperf/files/iperf-2.0.5.tar.gz/download)
+* nuttcp: GPL v2 License (http://nuttcp.net/nuttcp/beta/LICENSE, built from source code: http://nuttcp.net/nuttcp/beta/nuttcp-7.3.2.c)
 
 Redistribution of nuttcp and iperf is governed by their respective licenses. Please make sure you read and understand each one before further redistributing VMTP downstream.
 
+Required legal attachment for iperf binary distribution 
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+iperf 2.0.5: built from source code: https://sourceforge.net/projects/iperf/files/iperf-2.0.5.tar.gz/download
+
+(extract from the COPYING file as required by the iperf license, the full copy of the LICENSE is provided under legal/iperf)
+Copyright (c) 1999-2007, The Board of Trustees of the University of Illinois
+All Rights Reserved.
+
+Iperf performance test
+Mark Gates
+Ajay Tirumala
+Jim Ferguson
+Jon Dugan
+Feng Qin
+Kevin Gibbs
+John Estabrook
+National Laboratory for Applied Network Research
+National Center for Supercomputing Applications
+University of Illinois at Urbana-Champaign
+http://www.ncsa.uiuc.edu
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software (Iperf) and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation the
+rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+sell copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+Redistributions of source code must retain the above copyright notice, this
+list of conditions and the following disclaimers.
+
+Redistributions in binary form must reproduce the above copyright notice, this
+list of conditions and the following disclaimers in the documentation and/or
+other materials provided with the distribution.
+
+Neither the names of the University of Illinois, NCSA, nor the names of its
+contributors may be used to endorse or promote products derived from this
+Software without specific prior written permission.  THE SOFTWARE IS PROVIDED
+"AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
+LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+AND NONINFRINGEMENT. IN NO EVENT SHALL THE CONTIBUTORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Required legal attachment for nuttcp binary distribution 
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+nuttcp 7.3.2c: GPL v2 License (http://nuttcp.net/nuttcp/beta/LICENSE, built from unmodified source code: http://nuttcp.net/nuttcp/beta/nuttcp-7.3.2.c)
+A copy of the LICENSE file and source code (unmodifed) is provided in this repository (under legal/nuttcp), as required by the nuttcp license.
+
+Extract of interest related to the binary attachment:
+
+1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+  
+2. (Provision does not apply since the code is unmodified)
+
+3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
 Links
 -----
 
diff --git a/legal/iperf/COPYING b/legal/iperf/COPYING
new file mode 100644
index 0000000..9a8683d
--- /dev/null
+++ b/legal/iperf/COPYING
@@ -0,0 +1,40 @@
+Copyright (c) 1999-2007, The Board of Trustees of the University of Illinois
+All Rights Reserved.
+
+Iperf performance test
+Mark Gates
+Ajay Tirumala
+Jim Ferguson
+Jon Dugan
+Feng Qin
+Kevin Gibbs
+John Estabrook
+National Laboratory for Applied Network Research 
+National Center for Supercomputing Applications 
+University of Illinois at Urbana-Champaign 
+http://www.ncsa.uiuc.edu
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software (Iperf) and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation the
+rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+sell copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+
+Redistributions of source code must retain the above copyright notice, this
+list of conditions and the following disclaimers.
+
+Redistributions in binary form must reproduce the above copyright notice, this
+list of conditions and the following disclaimers in the documentation and/or
+other materials provided with the distribution.
+
+Neither the names of the University of Illinois, NCSA, nor the names of its
+contributors may be used to endorse or promote products derived from this
+Software without specific prior written permission.  THE SOFTWARE IS PROVIDED
+"AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
+LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+AND NONINFRINGEMENT. IN NO EVENT SHALL THE CONTIBUTORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/legal/nuttcp/LICENSE.txt b/legal/nuttcp/LICENSE.txt
new file mode 100644
index 0000000..d511905
--- /dev/null
+++ b/legal/nuttcp/LICENSE.txt
@@ -0,0 +1,339 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+	    How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/legal/nuttcp/nuttcp-7.3.2.c b/legal/nuttcp/nuttcp-7.3.2.c
new file mode 100644
index 0000000..bda5878
--- /dev/null
+++ b/legal/nuttcp/nuttcp-7.3.2.c
@@ -0,0 +1,9459 @@
+/*
+ *	N U T T C P . C						v7.3.2
+ *
+ * Copyright(c) 2000 - 2014 Bill Fink.  All rights reserved.
+ * Copyright(c) 2003 - 2014 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://lcp.nrl.navy.mil/nuttcp/
+ *
+ * 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.
+ *
+ * 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
+*/
+
+#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 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
+
+#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 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_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 BRIEF_RETRANS_STREAMS	0x2	/* brief per stream retrans 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 */
+
+#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 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 );
+
+int get_retrans( int sockfd );
+
+#if defined(linux) && defined(TCPI_OPT_TIMESTAMPS)
+void print_tcpinfo();
+#endif
+
+int vers_major = 7;
+int vers_minor = 3;
+int vers_delta = 2;
+int ivers;
+int rvers_major = 0;
+int rvers_minor = 0;
+int rvers_delta = 0;
+int irvers;
+int beta = 0;
+
+struct sockaddr_in sinme[MAXSTREAM + 1];
+struct sockaddr_in sinhim[MAXSTREAM + 1];
+struct sockaddr_in save_sinhim, save_mc;
+
+#ifdef AF_INET6
+struct sockaddr_in6 sinme6[MAXSTREAM + 1];
+struct sockaddr_in6 sinhim6[MAXSTREAM + 1];
+struct sockaddr_in6 save_sinhim6, save_mc6;
+struct in6_addr hi_mc6, hi_mc6_asm;
+#endif
+
+struct sockaddr_storage frominet;
+
+int domain = PF_UNSPEC;
+int af = AF_UNSPEC;
+int mc_af = AF_UNSPEC;
+int explicitaf = 0;		/* address family explicit specified (-4|-6) */
+int fd[MAXSTREAM + 1];		/* fd array of network sockets */
+int nfd;			/* fd for accept call */
+struct pollfd pollfds[MAXSTREAM + 4];	/* used for reading interval reports */
+socklen_t fromlen;
+
+int buflen = 64 * 1024;		/* length of buffer */
+int nbuflen;
+int mallocsize;
+char *buf;			/* ptr to dynamic buffer */
+unsigned long long nbuf = 0;	/* number of buffers to send in sinkmode */
+int nbuf_bytes = 0;		/* set to 1 if nbuf is actually bytes */
+
+/*  nick code  */
+int sendwin=0, sendwinval=0, origsendwin=0;
+socklen_t optlen;
+int rcvwin=0, rcvwinval=0, origrcvwin=0;
+int srvrwin=0;
+/*  end nick code  */
+
+#if defined(linux)
+int sendwinavail=0, rcvwinavail=0, winadjust=0;
+#endif
+
+#if defined(linux) && defined(TCPI_OPT_TIMESTAMPS)
+#ifdef OLD_TCP_INFO
+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
+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
+#endif
+
+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 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 */
+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 */
+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\
+	-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\
+	-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\
+	-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 */
+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 */
+
+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);
+	}
+
+	if (interval && !trans) {
+		/* Get real time */
+		gettimeofday(&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 (*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 ((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 (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;	/* time EOD packet was received */
+	struct timeval time_eod0;	/* time EOD0 packet was received */
+	struct timeval timed;		/* time delta */
+	struct timeval timeconn1;	/* time before connect() for RTT */
+	struct timeval timeconn2;	/* 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;
+	uint32_t total_retrans = 0;
+	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;
+			}
+			break;
+		case 'R':
+			reqval = 1;
+			haverateopt = 1;
+			if (argv[0][2] == 'i') {
+				cp1 = getoptvalp(argv, 3, reqval, &skiparg);
+				sscanf(cp1, "%lf", &rate_opt);
+				irate = 1;
+			}
+			else if (argv[0][2] == 'a') {
+				cp1 = getoptvalp(argv, 3, reqval, &skiparg);
+				sscanf(cp1, "%lf", &rate_opt);
+				irate = 0;
+			}
+			else if (argv[0][2] == 'u') {
+				rate_opt = 0.0;
+				irate = 0;
+				cp1 = &argv[0][3];
+			}
+			else {
+				cp1 = getoptvalp(argv, 2, reqval, &skiparg);
+				sscanf(cp1, "%lf", &rate_opt);
+			}
+			if ((cp2 = strchr(cp1, '/'))) {
+				*cp2++ = '\0';
+				maxburst = atoi(cp2);
+				if (maxburst <= 0) {
+					fprintf(stderr,
+						"invalid maxburst = %d\n",
+						maxburst);
+					fflush(stderr);
+					exit(1);
+				}
+			}
+			if (*cp1)
+				ch = *(cp1 + strlen(cp1) - 1);
+			else
+				ch = '\0';
+			if ((ch == 'm') || (ch == 'M'))
+				rate_opt *= 1000;
+			else if ((ch == 'g') || (ch == 'G'))
+				rate_opt *= 1000000;
+			else if (ch == 'p') {
+				rate_pps = 1;
+				if (strlen(cp1) >= 2) {
+					ch = *(cp1 + strlen(cp1) - 2);
+					if ((ch == 'k') || (ch == 'K'))
+						rate_opt *= 1000;
+					if ((ch == 'm') || (ch == 'M'))
+						rate_opt *= 1000000;
+				}
+			}
+			rate = rate_opt;
+			if (rate == 0)
+				rate = MAXRATE;
+			break;
+		case 'T':
+			reqval = 0;
+			cp1 = getoptvalp(argv, 2, reqval, &skiparg);
+			sscanf(cp1, "%lf", &timeout);
+			if (timeout < 0) {
+				fprintf(stderr, "invalid timeout = %f\n", timeout);
+				fflush(stderr);
+				exit(1);
+			}
+			else if (timeout == 0.0)
+				timeout = DEFAULT_TIMEOUT;
+			if (*cp1)
+				ch = *(cp1 + strlen(cp1) - 1);
+			else
+				ch = '\0';
+			if ((ch == 'm') || (ch == 'M'))
+				timeout *= 60.0;
+			else if ((ch == 'h') || (ch == 'H'))
+				timeout *= 3600.0;
+			else if ((ch == 'd') || (ch == 'D'))
+				timeout *= 86400.0;
+			itimer.it_value.tv_sec = timeout;
+			itimer.it_value.tv_usec =
+				(timeout - itimer.it_value.tv_sec)*1000000;
+			if (timeout && !nbuf)
+				nbuf = INT_MAX;
+			break;
+		case 'i':
+			reqval = 0;
+			cp1 = getoptvalp(argv, 2, reqval, &skiparg);
+			sscanf(cp1, "%lf", &interval);
+			if (interval < 0.0) {
+				fprintf(stderr, "invalid interval = %f\n", interval);
+				fflush(stderr);
+				exit(1);
+			}
+			else if (interval == 0.0)
+				interval = 1.0;
+			if (*cp1)
+				ch = *(cp1 + strlen(cp1) - 1);
+			else
+				ch = '\0';
+			if ((ch == 'm') || (ch == 'M'))
+				interval *= 60.0;
+			else if ((ch == 'h') || (ch == 'H'))
+				interval *= 3600.0;
+			break;
+		case 'I':
+			reqval = 1;
+			ident[0] = '-';
+			strncpy(&ident[1],
+				getoptvalp(argv, 2, reqval, &skiparg), 40);
+			ident[41] = '\0';
+			break;
+		case 'F':
+			reverse = 1;
+			break;
+		case 'b':
+			reqval = 0;
+			cp1 = getoptvalp(argv, 2, reqval, &skiparg);
+			if (*cp1) {
+				if (isalpha((int)(*cp1)))
+					brief = 1;
+				else
+					brief = atoi(cp1);
+				if (strchr(cp1, 'r'))
+					brief |= BRIEF_RETRANS_STREAMS;
+			}
+			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;
+			else if (strcmp(&argv[0][2], "debugretrans") == 0)
+				format |= DEBUGRETRANS;
+			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;
+			else if (strcmp(&argv[0][2], "parse") == 0)
+				format |= PARSE;
+			else if (strcmp(&argv[0][2], "-beta") == 0)
+				format |= NOBETAMSG;
+			/* below is for compatibility with 6.0.x beta */
+			else if (strcmp(&argv[0][2], "rtt") == 0)
+				format |= WANTRTT;
+			else if (strcmp(&argv[0][2], "-rtt") == 0)
+				format &= ~WANTRTT;
+			else {
+				if (argv[0][2]) {
+					fprintf(stderr, "invalid format option \"%s\"\n", &argv[0][2]);
+					fflush(stderr);
+					exit(1);
+				}
+				else {
+					fprintf(stderr, "invalid null format option\n");
+					fprintf(stderr, "perhaps the \"-F\" flip option was intended\n");
+					fflush(stderr);
+					exit(1);
+				}
+			}
+			break;
+		case 'x':
+			reqval = 1;
+			if (argv[0][2] == 't') {
+				traceroute = 1;
+				brief = 1;
+			}
+#ifdef HAVE_SETPRIO
+			else if (argv[0][2] == 'P') {
+				priority = atoi(getoptvalp(argv, 3, reqval,
+						&skiparg));
+			}
+#endif
+#ifdef HAVE_SETAFFINITY
+			else if (argv[0][2] == 'c') {
+				reqval = 1;
+				if (argv[0][3] == 's') {
+					cp1 = getoptvalp(argv, 4, reqval,
+							 &skiparg);
+					srvr_affinity = atoi(cp1);
+					if (srvr_affinity < 0) {
+						fprintf(stderr,
+							"invalid srvr_affinity "
+							"= %d\n",
+							srvr_affinity);
+						fflush(stderr);
+						exit(1);
+					}
+				}
+				else {
+					cp1 = getoptvalp(argv, 3, reqval,
+							 &skiparg);
+					affinity = atoi(cp1);
+					if ((affinity < 0) ||
+					    (affinity >= CPU_SETSIZE)) {
+						fprintf(stderr,
+							"invalid affinity "
+							"= %d\n", affinity);
+						fflush(stderr);
+						exit(1);
+					}
+					if ((cp2 = strchr(cp1, '/'))) {
+						srvr_affinity = atoi(cp2 + 1);
+						if (srvr_affinity < 0) {
+							fprintf(stderr,
+								"invalid "
+								"srvr_affinity "
+								"= %d\n",
+								srvr_affinity);
+							fflush(stderr);
+							exit(1);
+						}
+					}
+				}
+			}
+#endif
+			else {
+				if (argv[0][2]) {
+					fprintf(stderr, "invalid x option \"%s\"\n", &argv[0][2]);
+					fflush(stderr);
+					exit(1);
+				}
+				else {
+					fprintf(stderr, "invalid null x option\n");
+					fflush(stderr);
+					exit(1);
+				}
+			}
+			break;
+		case '3':
+			thirdparty = 1;
+			break;
+		case 'm':
+			reqval = 0;
+			if (argv[0][2] == 'a') {
+				ssm = 0;
+				cp1 = getoptvalp(argv, 3, reqval, &skiparg);
+			}
+			else if (argv[0][2] == 's') {
+#ifdef MCAST_JOIN_SOURCE_GROUP
+				ssm = 1;
+				cp1 = getoptvalp(argv, 3, reqval, &skiparg);
+#else
+				fprintf(stderr,
+					"This system does not support SSM\n");
+				fflush(stderr);
+				exit(1);
+#endif
+			}
+			else {
+				cp1 = getoptvalp(argv, 2, reqval, &skiparg);
+			}
+			if (*cp1)
+				mc_param = atoi(cp1);
+			else
+				mc_param = 1;
+			if ((mc_param < 1) || (mc_param > 255)) {
+				fprintf(stderr, "invalid multicast ttl = %d\n", mc_param);
+				fflush(stderr);
+				exit(1);
+			}
+			multicast = mc_param;
+			break;
+		case 'g':
+			reqval = 1;
+			mc_addr = getoptvalp(argv, 2, reqval, &skiparg);
+			break;
+		case 'M':
+			reqval = 1;
+			datamss = atoi(getoptvalp(argv, 2, reqval, &skiparg));
+			if (datamss < 0) {
+				fprintf(stderr, "invalid datamss = %d\n", datamss);
+				fflush(stderr);
+				exit(1);
+			}
+			break;
+		case 'c':
+			reqval = 1;
+			cp1 = getoptvalp(argv, 2, reqval, &skiparg);
+			tos = strtol(cp1, NULL, 0);
+			if (*cp1)
+				ch = *(cp1 + strlen(cp1) - 1);
+			else
+				ch = '\0';
+			if ((ch == 'p') || (ch == 'P')) {
+				/* Precedence */
+				if (tos > 7) {
+					fprintf(stderr, "invalid precedence = %d\n", tos);
+					fflush(stderr);
+					exit(1);
+				}
+				tos <<= 5;
+			}
+			else if ((ch != 't') && (ch != 'T')) {
+				/* DSCP */
+				if (tos > 63) {
+					fprintf(stderr, "invalid dscp = %d\n", tos);
+					fflush(stderr);
+					exit(1);
+				}
+				tos <<= 2;
+			}
+			if (tos > 255) {
+				fprintf(stderr, "invalid tos = %d\n", tos);
+				fflush(stderr);
+				exit(1);
+			}
+			break;
+		case 'a':
+			retry_server = 1;
+			break;
+		case '-':
+			if (strcmp(&argv[0][2], "nofork") == 0) {
+				nofork=1;
+			}
+			else if (strcmp(&argv[0][2], "no3rdparty") == 0) {
+				no3rd=1;
+			}
+			else if (strcmp(&argv[0][2],
+				 "idle-data-timeout") == 0) {
+				if ((cp1 = strchr(argv[1], '/'))) {
+					if (strchr(cp1 + 1, '/')) {
+						if (sscanf(argv[1],
+							"%lf/%lf/%lf",
+							&idle_data_min,
+							&default_idle_data,
+							&idle_data_max) != 3) {
+							fprintf(stderr, "error scanning idle-data-timeout parameter = %s\n", argv[1]);
+							fflush(stderr);
+							exit(1);
+						}
+						if (idle_data_min <= 0.0) {
+							fprintf(stderr, "invalid value for idle-data-timeout minimum = %f\n", idle_data_min);
+							fflush(stderr);
+							exit(1);
+						}
+						if (default_idle_data <= 0.0) {
+							fprintf(stderr, "invalid value for idle-data-timeout default = %f\n", default_idle_data);
+							fflush(stderr);
+							exit(1);
+						}
+						if (idle_data_max <= 0.0) {
+							fprintf(stderr, "invalid value for idle-data-timeout maximum = %f\n", idle_data_max);
+							fflush(stderr);
+							exit(1);
+						}
+						if (idle_data_max <
+							idle_data_min) {
+							fprintf(stderr, "error: idle-data-timeout maximum of %f < minimum of %f\n", idle_data_max, idle_data_min);
+							fflush(stderr);
+							exit(1);
+						}
+					}
+					else {
+						fprintf(stderr, "invalid idle-data-timeout parameter = %s\n", argv[1]);
+						fflush(stderr);
+						exit(1);
+					}
+				}
+				else {
+					sscanf(argv[1], "%lf", &idle_data_min);
+					if (idle_data_min <= 0.0) {
+						fprintf(stderr, "invalid value for idle-data-timeout = %f\n", idle_data_min);
+						fflush(stderr);
+						exit(1);
+					}
+					idle_data_max = idle_data_min;
+					default_idle_data = idle_data_min;
+				}
+				argv++;
+				argc--;
+			}
+			else if (strcmp(&argv[0][2], "single-threaded") == 0) {
+				single_threaded=1;
+			}
+			else if (strcmp(&argv[0][2], "packet-burst") == 0) {
+				maxburst = atoi(argv[1]);
+				if (maxburst <= 0) {
+					fprintf(stderr,
+						"invalid maxburst = %d\n",
+						maxburst);
+					fflush(stderr);
+					exit(1);
+				}
+				argv++;
+				argc--;
+			}
+#ifdef IPV6_V6ONLY
+			else if (strcmp(&argv[0][2], "disable-v4-mapped") == 0) {
+				v4mapped=0;
+			}
+			else if (strcmp(&argv[0][2], "enable-v4-mapped") == 0) {
+				v4mapped=1;
+			}
+#endif
+			else {
+				goto usage;
+			}
+			break;
+		case 'h':
+		default:
+			goto usage;
+		}
+		argv++;
+		argc--;
+		if (skiparg) {
+			argv++;
+			argc--;
+		}
+	}
+
+	if (argc > 2) goto usage;
+	if (trans && (argc < 1)) goto usage;
+	if (clientserver && (argc != 0)) goto usage;
+
+	if (!clientserver && !trans && (argc < 1)) {
+		fprintf(stderr,
+			"nuttcp: Warning: Using obsolete \"classic\" mode:\n");
+		fprintf(stderr,
+			"                 Automatically switching to "
+					  "oneshot server mode "
+					  "(\"nuttcp -1\")\n");
+		oneshot = 1;
+		trans = 0;
+		clientserver = 1;
+		brief = 0;
+		verbose = 1;
+	}
+
+	host3 = NULL;
+	if (argc == 2) {
+		host3 = argv[1];
+		if (strlen(host3) > HOSTNAMELEN) {
+			fprintf(stderr, "3rd party host '%s' too long\n", host3);
+			fflush(stderr);
+			exit(1);
+		}
+		cp1 = host3;
+		while (*cp1) {
+			if (!isalnum((int)(*cp1)) && (*cp1 != '-') && (*cp1 != '.')
+					   && (*cp1 != ':') && (*cp1 != '/')
+					   && (*cp1 != '+') && (*cp1 != '=')) {
+				fprintf(stderr, "invalid 3rd party host '%s'\n", host3);
+				fflush(stderr);
+				exit(1);
+			}
+			cp1++;
+		}
+	}
+
+	if (multicast) {
+		udp = 1;
+		if (!buflenopt) buflen = DEFAULT_MC_UDPBUFLEN;
+		nstream = 1;
+	}
+
+	if (mc_addr && !multicast) {
+		fprintf(stderr, "can't use \"-g\" option for non-multicast\n");
+		fflush(stderr);
+		exit(1);
+	}
+	if (mc_addr && !*mc_addr) {
+		fprintf(stderr, "no multicast IP address specified for "
+				"\"-g\" option\n");
+		fflush(stderr);
+		exit(1);
+	}
+
+#ifdef AF_INET6
+	if (!inet_pton(AF_INET6, HI_MC6, &hi_mc6)) {
+		err("inet_pton");
+	}
+	if (!inet_pton(AF_INET6, HI_MC6_ASM, &hi_mc6_asm)) {
+		err("inet_pton");
+	}
+#endif
+
+	if (udp && !haverateopt)
+		rate = DEFAULT_UDP_RATE;
+
+	bzero((char *)&frominet, sizeof(frominet));
+	bzero((char *)&clientaddr, sizeof(clientaddr));
+
+#ifdef AF_INET6
+	bzero((char *)&clientaddr6, sizeof(clientaddr6));
+	clientscope6 = 0;
+#endif
+
+	if (!nbuf) {
+		if (timeout == 0.0) {
+			if (sinkmode) {
+				timeout = DEFAULT_TIMEOUT;
+				itimer.it_value.tv_sec = timeout;
+				itimer.it_value.tv_usec =
+					(timeout - itimer.it_value.tv_sec)
+						*1000000;
+			}
+			nbuf = INT_MAX;
+		}
+	}
+
+	if (srvrwin == -1) {
+		srvrwin = sendwin;
+	}
+
+	if ((argc == 0) && !explicitaf) {
+		domain = PF_INET;
+		af = AF_INET;
+	}
+
+	if (multilink) {
+		if (nstream == 1) {
+			fprintf(stderr, "Warning: multilink mode not meaningful for a single stream\n");
+			fflush(stderr);
+		}
+	}
+
+	if (argc >= 1) {
+		host = argv[0];
+		if ((cp1 = strchr(host, '+'))) {
+			*cp1++ = '\0';
+			if (*cp1)
+				stride = cp1;
+		}
+		if (multilink) {
+			if (stride) {
+				fprintf(stderr, "don't use both multilink and address stride\n");
+				fflush(stderr);
+				exit(1);
+			}
+			if ((cp1 = strchr(host, '/')) && strchr(cp1 + 1, '/')) {
+				fprintf(stderr, "multilink mode not compatible with multiple hosts %s\n", host);
+				fflush(stderr);
+				exit(1);
+			}
+		}
+		hostaddr = NULL;
+		implicit_hostaddr = 0;
+		if ((cp1 = strchr(host, '='))) {
+			*cp1++ = '\0';
+			if (strchr(cp1, '/')) {
+				fprintf(stderr, "host=addr format not supported for multiple control/data paths\n");
+				fflush(stderr);
+				exit(1);
+			}
+			if (*cp1) {
+				if (*cp1 == '=') {
+					implicit_hostaddr = 1;
+					cp1++;
+				}
+				hostaddr = cp1;
+				if (!implicit_hostaddr)
+					host = hostaddr;
+			}
+		}
+		stream_idx = 0;
+		res[0] = NULL;
+		cp1 = host;
+		if (host[strlen(host) - 1] == '/') {
+			fprintf(stderr, "bad hostname or address: trailing '/' not allowed: %s\n", host);
+			fflush(stderr);
+			exit(1);
+		}
+		if (strchr(host, '/') && !trans && !reverse) {
+			fprintf(stderr, "multiple control/data paths not supported for receive\n");
+			fflush(stderr);
+			exit(1);
+		}
+		if (strchr(host, '/') && trans && reverse) {
+			fprintf(stderr, "multiple control/data paths not supported for flipped transmit\n");
+			fflush(stderr);
+			exit(1);
+		}
+		if (host[0] == '/') {
+			host++;
+			cp1++;
+			stream_idx = 1;
+		}
+		else if ((cp2 = strchr(host, '/'))) {
+			host = cp2 + 1;
+		}
+
+		while (stream_idx <= nstream) {
+			bzero(&hints, sizeof(hints));
+			res[stream_idx] = NULL;
+			if (explicitaf) hints.ai_family = af;
+			if (udp)
+				hints.ai_socktype = SOCK_DGRAM;
+			else
+				hints.ai_socktype = SOCK_STREAM;
+			if ((cp2 = strchr(cp1, '/'))) {
+				if (stream_idx == nstream) {
+					fprintf(stderr, "bad hostname or address: too many data paths for nstream=%d: %s\n", nstream, argv[0]);
+					fflush(stderr);
+					exit(1);
+				}
+				*cp2 = '\0';
+			}
+			if (!(multilink && (stream_idx > 1)) &&
+			    (error_num = getaddrinfo(cp1, NULL, &hints,
+						     &res[stream_idx]))) {
+				if (implicit_hostaddr && hostaddr) {
+					if (res[stream_idx]) {
+						freeaddrinfo(res[stream_idx]);
+						res[stream_idx] = NULL;
+					}
+					error_num =
+						getaddrinfo(hostaddr, NULL,
+							    &hints,
+							    &res[stream_idx]);
+				}
+				if (error_num) {
+					if (cp2)
+						*cp2++ = '/';
+					if (hostaddr) {
+						if (implicit_hostaddr)
+							*(hostaddr - 2) = '=';
+						else
+							*(hostaddr - 1) = '=';
+					}
+					fprintf(stderr, "bad hostname or address: %s: %s\n", gai_strerror(error_num), argv[0]);
+					fflush(stderr);
+					exit(1);
+				}
+				if (implicit_hostaddr && hostaddr &&
+				    (stream_idx == 1)) {
+					if (stride)
+						*(stride - 1) = '+';
+					cp3 = hostaddr;
+					while (*cp3) {
+						*(cp3 - 1) = *cp3;
+						cp3++;
+					}
+					*(cp3 - 1) = '\0';
+					hostaddr--;
+					implicit_hostaddr = 0;
+					if (stride) {
+						stride--;
+						*(stride - 1) = '\0';
+					}
+				}
+			}
+			else if (multilink && (stream_idx > 1)) {
+				if (res[stream_idx - 1]->ai_next)
+					res[stream_idx] =
+						res[stream_idx - 1]->ai_next;
+				else
+					res[stream_idx] = res[1];
+			}
+			else if (!(multilink && (stream_idx > 1)) &&
+				 implicit_hostaddr && hostaddr) {
+				if (stride) {
+					strcat(host, "+");
+					strncat(host, stride, ADDRSTRLEN);
+					*(hostaddr - 2) = '\0';
+					stride = hostaddr - 1;
+				}
+				hostaddr = NULL;
+			}
+			af = res[stream_idx]->ai_family;
+/*
+ * At the moment PF_ matches AF_ but are maintained seperate and the socket
+ * call is supposed to be PF_
+ *
+ * For now we set domain from the address family we looked up, but if these
+ * ever get changed to not match some code will have to go here to find the
+ * domain appropriate for the family
+ */
+			domain = af;
+			stream_idx++;
+			if (cp2) {
+				*cp2++ = '/';
+				cp1 = cp2;
+			}
+			else
+				cp1 = host;
+		}
+		if (!res[0]) {
+			if ((cp1 = strchr(host, '/')))
+				*cp1 = '\0';
+			if ((error_num = getaddrinfo(host, NULL, &hints, &res[0]))) {
+				if (cp1)
+					*cp1++ = '/';
+				fprintf(stderr, "bad hostname or address: %s: %s\n", gai_strerror(error_num), argv[0]);
+				fflush(stderr);
+				exit(1);
+			}
+			af = res[0]->ai_family;
+			/* see previous comment about domain */
+			domain = af;
+			if (cp1)
+				*cp1 = '/';
+		}
+		if (hostaddr) {
+			host = argv[0];
+			if (implicit_hostaddr)
+				*(hostaddr - 2) = '=';
+			else
+				*(hostaddr - 1) = '=';
+		}
+	}
+
+	ipad_stride.ip32 = 0;
+	if (stride) {
+		if (strlen(stride) >= ADDRSTRLEN) {
+			fprintf(stderr, "address stride '%s' too long\n", stride);
+			fflush(stderr);
+			exit(1);
+		}
+		if (nstream == 1) {
+			fprintf(stderr, "Warning: stride %s not meaningful for a single stream\n", stride);
+			fflush(stderr);
+		}
+		if (udp) {
+			fprintf(stderr, "stride %s not valid for UDP\n",
+				stride);
+			fflush(stderr);
+			exit(1);
+		}
+		if ((cp1 = strchr(argv[0], '/')) && strchr(cp1 + 1, '/')) {
+			fprintf(stderr, "stride %s not compatible with multiple hosts %s\n", stride, argv[0]);
+			fflush(stderr);
+			exit(1);
+		}
+		if (af == AF_INET) {
+			if (strchr(stride, '.')) {
+				error_num = inet_pton(AF_INET, stride,
+						ipad_stride.buf);
+				if (error_num == 0) {
+					fprintf(stderr,
+						"stride %s not in correct presentation format\n",
+						stride);
+					fflush(stderr);
+					exit(1);
+				}
+				else if (error_num < 0)
+					err("inet_pton: stride");
+			}
+			else {
+				ipad_stride.ip32 = atoi(stride);
+				ipad_stride.ip32 = htonl(ipad_stride.ip32);
+			}
+		}
+		else {
+			fprintf(stderr, "stride %s not valid for IPv6\n",
+				stride);
+			fflush(stderr);
+			exit(1);
+		}
+		*(stride - 1) = '+';
+	}
+
+	if (host3 && !strchr(host3, '=') && !strchr(host3, '/')) {
+		cp1 = strchr(host3, '+');
+		if (cp1) {
+			if (strlen(cp1 + 1) >= ADDRSTRLEN) {
+				fprintf(stderr, "3rd party address stride '%s' too long\n", cp1 + 1);
+				fflush(stderr);
+				exit(1);
+			}
+			*cp1 = '\0';
+		}
+		if (inet_pton(af, host3, &dummy) != 1) {
+			bzero(&hints, sizeof(hints));
+			hints.ai_family = af;
+			if (udp)
+				hints.ai_socktype = SOCK_DGRAM;
+			else
+				hints.ai_socktype = SOCK_STREAM;
+			host3res = NULL;
+			error_num = getaddrinfo(host3, NULL, &hints, &host3res);
+			if (error_num == 0) {
+				nameinfo_flags = NI_NUMERICHOST;
+				error_num = getnameinfo(host3res->ai_addr,
+							host3res->ai_addrlen,
+							host3addr, ADDRSTRLEN,
+							NULL, 0,
+							nameinfo_flags);
+			}
+			if (host3res) {
+				freeaddrinfo(host3res);
+				host3res = NULL;
+			}
+			if (error_num == 0) {
+				strncpy(host3buf, host3, HOSTNAMELEN);
+				strcat(host3buf, "==");
+				strncat(host3buf, host3addr, ADDRSTRLEN);
+				if (cp1) {
+					strcat(host3buf, "+");
+					strncat(host3buf, cp1 + 1, ADDRSTRLEN);
+				}
+				host3 = host3buf;
+			}
+		}
+		if (cp1)
+			*cp1 = '+';
+	}
+
+	if (!port) {
+		if (af == AF_INET) {
+			if ((sp = getservbyname( "nuttcp-data", "tcp" )))
+				port = ntohs(sp->s_port);
+			else
+				port = DEFAULT_PORT;
+		}
+#ifdef AF_INET6
+		else if (af == AF_INET6) {
+			if ((sp = getservbyname( "nuttcp6-data", "tcp" )))
+				port = ntohs(sp->s_port);
+			else {
+				if ((sp = getservbyname( "nuttcp-data", "tcp" )))
+					port = ntohs(sp->s_port);
+				else
+					port = DEFAULT_PORT;
+			}
+		}
+#endif
+		else {
+			err("unsupported AF");
+		}
+	}
+
+	if (!ctlport) {
+		if (af == AF_INET) {
+			if ((sp = getservbyname( "nuttcp", "tcp" )))
+				ctlport = ntohs(sp->s_port);
+			else
+				ctlport = DEFAULT_CTLPORT;
+		}
+#ifdef AF_INET6
+		else if (af == AF_INET6) {
+			if ((sp = getservbyname( "nuttcp6", "tcp" )))
+				ctlport = ntohs(sp->s_port);
+			else {
+				if ((sp = getservbyname( "nuttcp", "tcp" )))
+					ctlport = ntohs(sp->s_port);
+				else
+					ctlport = DEFAULT_CTLPORT;
+			}
+		}
+#endif
+		else {
+			err("unsupported AF");
+		}
+	}
+
+	if ((port < 1024) || ((port + nstream - 1) > 65535)) {
+		fprintf(stderr, "invalid port/nstream = %d/%d\n", port, nstream);
+		fflush(stderr);
+		exit(1);
+	}
+
+	if ((ctlport >= port) && (ctlport <= (port + nstream - 1))) {
+		fprintf(stderr, "ctlport = %d overlaps port/nstream = %d/%d\n", ctlport, port, nstream);
+		fflush(stderr);
+		exit(1);
+	}
+
+	if (timeout && (interval >= timeout)) {
+		fprintf(stderr, "ignoring interval=%f which is greater than or equal timeout=%f\n", interval, timeout);
+		fflush(stderr);
+		interval = 0;
+	}
+
+	if (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;
+			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);
+		fprintf(stdout, "initial system retrans = %d\n", sretrans);
+	}
+	nretrans[0] = 0;
+
+	for ( stream_idx = 1; stream_idx <= nstream; stream_idx++ ) {
+		fd[stream_idx] = -1;
+		nretrans[stream_idx] = 0;
+	}
+
+	for ( stream_idx = start_idx; stream_idx <= nstream; stream_idx++ ) {
+		if (clientserver && (stream_idx == 1)) {
+			retransinfo = 0;
+			if (nstream == 1) {
+				send_retrans = 1;
+				read_retrans = 1;
+			}
+			do_retrans = 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;
+					}
+				}
+				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 (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;
+				}
+				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;
+				}
+				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 (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 (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]);
+				if (retransinfo > 0)
+					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 (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 (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 (setsockopt(fd[stream_idx], IPPROTO_IP, IP_TOS,
+					(void *)&tos, sizeof(tos)) < 0)
+					err("setsockopt");
+			}
+			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 (setsockopt(fd[stream_idx], IPPROTO_IP, IP_TOS,
+					(void *)&tos, sizeof(tos)) < 0)
+					err("setsockopt");
+			}
+		    }
+		}
+		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)
+				gettimeofday(&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) {
+				gettimeofday(&timeconn2, (struct timezone *)0);
+				tvsub( &timeconn, &timeconn2, &timeconn1 );
+				rtt = timeconn.tv_sec*1000 +
+						((double)timeconn.tv_usec)/1000;
+			}
+			if (sockopterr && trans &&
+			    (stream_idx > 0) && datamss) {
+				optlen = sizeof(datamss);
+				if ((sockopterr = setsockopt(fd[stream_idx], IPPROTO_TCP, TCP_MAXSEG,  (void *)&datamss, optlen)) < 0) {
+					if (errno != EINVAL)
+						err("unable to set maximum segment size");
+					else
+						err("setting maximum segment size not supported on this OS");
+				}
+			}
+			if (stream_idx == nstream) {
+				optlen = sizeof(datamss);
+				if (getsockopt(fd[stream_idx], IPPROTO_TCP, TCP_MAXSEG,  (void *)&datamss, &optlen) < 0)
+					err("get dataconn maximum segment size didn't work");
+				if (format & DEBUGMTU)
+					fprintf(stderr, "datamss = %d\n", datamss);
+			}
+			if ((stream_idx == nstream) && (brief <= 0)) {
+				char tmphost[ADDRSTRLEN] = "\0";
+				if (af == AF_INET) {
+				    inet_ntop(af, &sinhim[stream_idx].sin_addr.s_addr,
+					      tmphost, sizeof(tmphost));
+				}
+#ifdef AF_INET6
+				else if (af == AF_INET6) {
+				    inet_ntop(af, sinhim6[stream_idx].sin6_addr.s6_addr,
+					      tmphost, sizeof(tmphost));
+				}
+#endif
+				else {
+				    err("unsupported AF");
+				}
+
+				if (format & PARSE) {
+					fprintf(stdout,
+						"nuttcp%s%s: connect=%s",
+						trans?"-t":"-r", ident,
+						tmphost);
+					if (trans && datamss) {
+						fprintf(stdout, " mss=%d",
+							datamss);
+					}
+					if (rtt)
+						fprintf(stdout, P_RTT_FMT, rtt);
+				}
+				else {
+					fprintf(stdout,
+						"nuttcp%s%s: connect to %s",
+						trans?"-t":"-r", ident,
+						tmphost);
+					if (rtt || (trans && datamss))
+						fprintf(stdout, " with");
+					if (trans && datamss) {
+						fprintf(stdout, " mss=%d",
+							datamss);
+						if (rtt)
+							fprintf(stdout, ",");
+					}
+					if (rtt)
+						fprintf(stdout, RTT_FMT, rtt);
+				}
+				fprintf(stdout, "\n");
+			}
+		    } else {
+			/* The receiver listens for the connection
+			 * (unless reversed by the flip option)
+			 */
+			if (trans && (stream_idx > 0) && datamss) {
+#if defined(__CYGWIN__) || defined(_WIN32)
+				if (format & PARSE)
+					fprintf(stderr, "nuttcp%s%s: Warning=\"setting_maximum_segment_size_not_supported_on_windows\"\n",
+						trans?"-t":"-r", ident);
+				else
+					fprintf(stderr, "nuttcp%s%s: Warning: setting maximum segment size not supported on windows\n",
+						trans?"-t":"-r", ident);
+				fflush(stderr);
+#endif
+				optlen = sizeof(datamss);
+				if ((sockopterr = setsockopt(fd[stream_idx], IPPROTO_TCP, TCP_MAXSEG,  (void *)&datamss, optlen)) < 0)
+					if (errno != EINVAL)
+						err("unable to set maximum segment size");
+			}
+			listen(fd[stream_idx], LISTEN_BACKLOG);
+			if (clientserver && !client && (stream_idx == 0)
+					 && !inetd && !nofork && !forked) {
+				if ((pid = fork()) == (pid_t)-1)
+					err("can't fork");
+				if (pid != 0)
+					exit(0);
+				forked = 1;
+				if (sinkmode) {
+					close(0);
+					close(1);
+					close(2);
+					open("/dev/null", O_RDWR);
+					dup(0);
+					dup(0);
+				}
+				setsid();
+			}
+			if (options && (stream_idx > 0)) {
+				if (setsockopt(fd[stream_idx], SOL_SOCKET, options, (void *)&one, sizeof(one)) < 0)
+					errmes("unable to setsockopt options");
+			}
+			if (sockopterr && trans &&
+			    (stream_idx > 0) && datamss) {
+				optlen = sizeof(datamss);
+				if ((sockopterr = setsockopt(fd[stream_idx], IPPROTO_TCP, TCP_MAXSEG,  (void *)&datamss, optlen)) < 0)
+					if (errno != EINVAL)
+						err("unable to set maximum segment size");
+			}
+			if (clientserver && (stream_idx > 0)) {
+				sigact.sa_handler = ignore_alarm;
+				sigemptyset(&sigact.sa_mask);
+				sigact.sa_flags = 0;
+				sigaction(SIGALRM, &sigact, &savesigact);
+				alarm(ACCEPT_TIMEOUT);
+			}
+acceptnewconn:
+			fromlen = sizeof(frominet);
+			nfd=accept(fd[stream_idx], (struct sockaddr *)&frominet, &fromlen);
+			save_errno = errno;
+			if (clientserver && (stream_idx > 0)) {
+				alarm(0);
+				sigact.sa_handler = savesigact.sa_handler;
+				sigact.sa_mask = savesigact.sa_mask;
+				sigact.sa_flags = savesigact.sa_flags;
+				sigaction(SIGALRM, &sigact, 0);
+			}
+			if (nfd < 0) {
+				/* check for interrupted system call - on
+				 * server, close data streams, cleanup and
+				 * try again - all other errors just die
+				 */
+				if ((save_errno == EINTR) && clientserver
+							  && (stream_idx > 0)) {
+					if (client) {
+						/* if client, just give nice
+						 * error message and exit
+						 */
+						mes("Error: accept() timeout");
+						exit(1);
+					}
+					for ( i = 1; i <= stream_idx; i++ )
+						close(fd[i]);
+					goto cleanup;
+				}
+				err("accept");
+			}
+			if (clientserver && !client && (stream_idx == 0)
+					 && !inetd && !nofork
+					 && !single_threaded) {
+				/* multi-threaded manually started server */
+				if ((pid = fork()) == (pid_t)-1)
+					err("can't fork");
+				if (pid != 0) {
+					/* parent just waits for quick
+					 * child exit */
+					while ((wait_pid = wait(&pidstat))
+						    != pid) {
+						if (wait_pid == (pid_t)-1) {
+							if (errno == ECHILD)
+								break;
+							err("wait failed");
+						}
+					}
+					/* and then accept()s another client
+					 * connection */
+					close(nfd);
+					stream_idx = 0;
+					if (oneshot)
+						exit(0);
+					goto acceptnewconn;
+				}
+				/* child just makes a grandchild and then
+				 * immediately exits (avoid zombie processes) */
+				if ((pid = fork()) == (pid_t)-1)
+					err("can't fork");
+				if (pid != 0)
+					exit(0);
+				/* grandkid does all the work */
+				oneshot = 1;
+			}
+			af = frominet.ss_family;
+			close(fd[stream_idx]);
+			fd[stream_idx]=nfd;
+			if (sockopterr && trans &&
+			    (stream_idx > 0) && datamss) {
+				optlen = sizeof(datamss);
+				if ((sockopterr = setsockopt(fd[stream_idx], IPPROTO_TCP, TCP_MAXSEG,  (void *)&datamss, optlen)) < 0) {
+					if (errno != EINVAL)
+						err("unable to set maximum segment size");
+					else
+						err("setting maximum segment size not supported on this OS");
+				}
+			}
+			if (stream_idx == nstream) {
+				optlen = sizeof(datamss);
+				if (getsockopt(fd[stream_idx], IPPROTO_TCP, TCP_MAXSEG,  (void *)&datamss, &optlen) < 0)
+					err("get dataconn maximum segment size didn't work");
+				if (format & DEBUGMTU)
+					fprintf(stderr, "datamss = %d\n", datamss);
+			}
+			if (af == AF_INET) {
+			    struct sockaddr_in peer;
+			    socklen_t peerlen = sizeof(peer);
+			    if (getpeername(fd[stream_idx],
+					    (struct sockaddr *)&peer,
+					    &peerlen) < 0) {
+				err("getpeername");
+			    }
+			    if ((stream_idx == nstream) && (brief <= 0)) {
+				char tmphost[ADDRSTRLEN] = "\0";
+				inet_ntop(af, &peer.sin_addr.s_addr,
+					  tmphost, sizeof(tmphost));
+
+				if (format & PARSE) {
+					fprintf(stdout,
+						"nuttcp%s%s: accept=%s",
+						trans?"-t":"-r", ident,
+						tmphost);
+					if (trans && datamss) {
+						fprintf(stdout, " mss=%d",
+							datamss);
+					}
+				}
+				else {
+					fprintf(stdout,
+						"nuttcp%s%s: accept from %s",
+						trans?"-t":"-r", ident,
+						tmphost);
+					if (trans && datamss) {
+						fprintf(stdout, " with mss=%d",
+							datamss);
+					}
+				}
+				fprintf(stdout, "\n");
+			    }
+			    if (stream_idx == 0) {
+				clientaddr = peer.sin_addr;
+				client_ipaddr.ss.ss_family = AF_INET;
+				client_ipaddr.sin.sin_addr = clientaddr;
+			    }
+			}
+#ifdef AF_INET6
+			else if (af == AF_INET6) {
+			    struct sockaddr_in6 peer;
+			    socklen_t peerlen = sizeof(peer);
+			    if (getpeername(fd[stream_idx],
+					    (struct sockaddr *)&peer,
+					    &peerlen) < 0) {
+				err("getpeername");
+			    }
+			    if ((stream_idx == nstream) && (brief <= 0)) {
+				char tmphost[ADDRSTRLEN] = "\0";
+				inet_ntop(af, peer.sin6_addr.s6_addr,
+					  tmphost, sizeof(tmphost));
+				if (format & PARSE) {
+				    fprintf(stdout,
+					    "nuttcp%s%s: accept=%s",
+					    trans?"-t":"-r", ident,
+					    tmphost);
+				    if (trans && datamss) {
+					fprintf(stdout, " mss=%d", datamss);
+				    }
+				}
+				else {
+				    fprintf(stdout,
+					    "nuttcp%s%s: accept from %s",
+					    trans?"-t":"-r", ident,
+					    tmphost);
+				    if (trans && datamss) {
+					fprintf(stdout, " with mss=%d",
+						datamss);
+				    }
+				}
+				fprintf(stdout, "\n");
+			    }
+			    if (stream_idx == 0) {
+				clientaddr6 = peer.sin6_addr;
+				clientscope6 = peer.sin6_scope_id;
+				client_ipaddr.ss.ss_family = AF_INET6;
+				client_ipaddr.sin6.sin6_addr = clientaddr6;
+			    }
+			}
+#endif
+			else {
+			    err("unsupported AF");
+			}
+		    }
+		}
+		if (!udp && trans && (stream_idx >= 1) && (retransinfo > 0)) {
+			if ((stream_idx == 1) || (retransinfo == 1)) {
+				nretrans[stream_idx] =
+					get_retrans(fd[stream_idx]);
+				iretrans[stream_idx] = nretrans[stream_idx];
+			}
+		}
+		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);
+#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%lup",
+							irate ? "i" : "", rate);
+					else
+						sprintf(tmpargs[j], "-R%s%lu",
+							irate ? "i" : "", 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 & 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;
+	}
+	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 && interval && !(format & NORETRANS) &&
+			    (nstream == 1) &&
+			    ((retransinfo == 1) ||
+			     ((retransinfo >= 2) &&
+			      (force_retrans >= retransinfo)))) {
+				uint32_t tmp;
+
+				if (client) {
+					if (send_retrans)
+						do_retrans = 1;
+					send_retrans = 0;
+					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 (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]);
+					nretrans[1] -= iretrans[1];
+					bcopy(&nretrans[1], buf + 24, 4);
+				}
+				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,
+								"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]);
+						    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_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) {
+						    gettimeofday(&time_eod0,
+							(struct timezone *)0);
+						    got_eod0 = 1;
+						    done = 1;
+						    continue;
+					    }
+					    if (strncmp(buf, "EOD", 3) == 0) {
+						    ocorrection = buf[3] - '0';
+						    gettimeofday(&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 */
+					    gettimeofday(&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) {
+						    gettimeofday(
+							&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) {
+						gettimeofday(&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) {
+						    gettimeofday(
+							&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;
+						    b_flag = 1;
+					    }
+					    else if (tmp == 0x48525452u) {
+						    /* "HRTR" */
+						    retransinfo = 2;
+						    b_flag = 1;
+					    }
+					    else if (tmp == 0x4E525452u) {
+						    /* "NRTR" */
+						    need_swap = 1;
+						    retransinfo = 1;
+						    b_flag = 1;
+					    }
+					    else if (tmp == 0x52545248u) {
+						    /* "RTRH" */
+						    need_swap = 1;
+						    retransinfo = 2;
+						    b_flag = 1;
+					    }
+					    else {
+						    retransinfo = -1;
+						    read_retrans = 0;
+					    }
+					}
+					else
+					    read_retrans = 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--;
+					    }
+				    }
+				    stream_idx++;
+				    stream_idx = stream_idx % nstream;
+			    }
+			    if (intr && (cnt > 0))
+				    nbytes += cnt;
+			}
+		}
+	} else {
+		register int cnt;
+		if (trans) {
+#if defined(linux)
+			struct stat instat;
+
+			if (fstat(savestdin, &instat) == 0) {
+				if (!S_ISREG(instat.st_mode)) {
+					zerocopy = 0;
+					directio = 0;
+				}
+			}
+			else {
+				zerocopy = 0;
+				directio = 0;
+			}
+
+			if (directio) {
+				flags = fcntl(savestdin, F_GETFL, 0);
+				if (flags < 0)
+					errmes("fcntl get O_DIRECT");
+				else {
+					flags |= O_DIRECT;
+					if (fcntl(savestdin,
+						  F_SETFL, flags) < 0)
+						errmes("fcntl set O_DIRECT");
+				}
+			}
+
+			if (zerocopy) {
+				while (nbuf-- &&
+				       ((cnt=sendfile(fd[stream_idx + 1],
+						      savestdin,
+						      (off_t *)&nbytes,
+						      buflen)) > 0)) {
+					cnt = 0;
+					stream_idx++;
+					stream_idx = stream_idx % nstream;
+				}
+			}
+			else
+#endif
+			{
+				while (nbuf-- &&
+				       ((cnt=read(savestdin, buf, buflen)) > 0) &&
+				       (Nwrite(fd[stream_idx + 1], buf, cnt)
+						== cnt)) {
+					nbytes += cnt;
+					cnt = 0;
+					stream_idx++;
+					stream_idx = stream_idx % nstream;
+				}
+			}
+			if (udp) {
+				strcpy(buf, "EOD0");
+				(void)Nwrite( fd[stream_idx + 1], buf, 4 ); /* rcvr end */
+			}
+		} else {
+#if defined(linux)
+			struct stat outstat;
+
+			if (fstat(savestdout, &outstat) == 0) {
+				if (!S_ISREG(outstat.st_mode))
+					directio = 0;
+			}
+			else
+				directio = 0;
+
+			if (directio) {
+				flags = fcntl(savestdout, F_GETFL, 0);
+				if (flags < 0)
+					errmes("fcntl get O_DIRECT");
+				else {
+					flags |= O_DIRECT;
+					if (fcntl(savestdout,
+						  F_SETFL, flags) < 0)
+						errmes("fcntl set O_DIRECT");
+				}
+			}
+#endif
+			while ((cnt=Nread(fd[stream_idx + 1], buf, buflen)) > 0 &&
+			    ((cnt != 4) || strncmp(buf, "EOD", 3) != 0) &&
+			    mwrite(savestdout, buf, cnt,
+				   cnt != buflen) == cnt) {
+				nbytes += cnt;
+				cnt = 0;
+				stream_idx++;
+				stream_idx = stream_idx % nstream;
+			}
+		}
+	}
+	if (errno && (errno != EAGAIN)) {
+		if ((errno != EINTR) &&
+#ifdef ERESTART
+		    (errno != ERESTART) &&
+#endif
+		    (!clientserver || client)) err("IO");
+	}
+	itimer.it_value.tv_sec = 0;
+	itimer.it_value.tv_usec = 0;
+	itimer.it_interval.tv_sec = 0;
+	itimer.it_interval.tv_usec = 0;
+	setitimer(ITIMER_REAL, &itimer, 0);
+	done = 1;
+	(void)read_timer(stats, sizeof(stats));
+	if (udp&&trans) {
+		usleep(500000);
+		strcpy(buf, "EOD1");
+		(void)Nwrite( fd[stream_idx + 1], buf, 4 ); /* rcvr end */
+		stream_idx++;
+		stream_idx = stream_idx % nstream;
+		usleep(500000);
+		strcpy(buf, "EOD2");
+		(void)Nwrite( fd[stream_idx + 1], buf, 4 ); /* rcvr end */
+		stream_idx++;
+		stream_idx = stream_idx % nstream;
+		usleep(500000);
+		strcpy(buf, "EOD3");
+		(void)Nwrite( fd[stream_idx + 1], buf, 4 ); /* rcvr end */
+		stream_idx++;
+		stream_idx = stream_idx % nstream;
+		usleep(500000);
+		strcpy(buf, "EOD4");
+		(void)Nwrite( fd[stream_idx + 1], buf, 4 ); /* rcvr end */
+		stream_idx++;
+		stream_idx = stream_idx % nstream;
+	}
+
+	if (!udp && trans && (format & DEBUGRETRANS)) {
+		sretrans = get_retrans(-1);
+		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;
+			}
+			if (ioctl(fd[stream_idx], SIOCOUTQ, &unsent) < 0) {
+				mes("couldn't get SIOCOUTQ value\n");
+				unsent = -1;
+			}
+			gettimeofday(&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);
+					gettimeofday(&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]);
+						    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_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;
+				}
+				if (ioctl(fd[stream_idx], SIOCOUTQ,
+					  &unsent) < 0) {
+					mes("couldn't get SIOCOUTQ value\n");
+					unsent = -1;
+				}
+				gettimeofday(&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;
+			}
+			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]);
+					nretrans[stream_idx] -=
+						iretrans[stream_idx];
+				}
+			}
+		}
+	}
+	if (!udp && trans && (format & DEBUGRETRANS)) {
+		sretrans = get_retrans(-1);
+		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]);
+				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_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;
+		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);
+				}
+			}
+			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 && !got_srvr_retrans)
+			retransinfo = -1;
+	}
+
+	if (!udp && trans && (retransinfo > 0)) {
+		total_retrans = 0;
+		for ( stream_idx = 1; stream_idx <= nstream; stream_idx++ ) {
+			total_retrans += nretrans[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, " )");
+			}
+			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);
+					    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 (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 (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);
+		if (!inetd)
+			close(fd[0]);
+		if (!udp && trans && (retransinfo > 0)) {
+			if (format & DEBUGRETRANS) {
+				sretrans = get_retrans(-1);
+				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;
+		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;
+		force_retrans = 0;
+		rtt = 0.0;
+		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;
+		}
+		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);
+	}
+}
+
+/*
+ *			P R E P _ T I M E R
+ */
+void
+prep_timer()
+{
+	gettimeofday(&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);
+	gettimeofday(&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);
+		gettimeofday(&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;
+
+	if (irate) {
+		/* Get real time */
+		gettimeofday(&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) {
+			while ((maxburst*(double)count/rate/125 > deltat)
+			       && !intr) {
+				/* Get real time */
+				gettimeofday(&timedol, (struct timezone *)0);
+				tvsub( &td, &timedol, &timepk );
+				deltat = td.tv_sec + ((double)td.tv_usec)
+							/ 1000000;
+			}
+		}
+
+		if (nburst > maxburst) {
+			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 */
+			gettimeofday(&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 */
+		gettimeofday(&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 {
+		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 )
+{
+	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(tcpinf);
+		if (getsockopt(sockfd, SOL_TCP, TCP_INFO, (void *)&tcpinf,
+			       &optlen) == 0) {
+			if (optlen >= SIZEOF_TCP_INFO_RETRANS) {
+				retransinfo = 1;
+				b_flag = 1;
+				return(tcpinf.tcpi_total_retrans);
+			}
+		}
+		if (retransinfo == 1) {
+			retransinfo = -1;
+			return(0);
+		}
+		retransinfo = 2;
+	}
+#endif
+
+	if ((retransinfo == 3) || (!(proc_snmp = fopen(PROC_SNMP, "r")))) {
+		retransinfo = 3;
+		if (pipe(pipefd) != 0) {
+			retransinfo = -1;
+			return(0);
+		}
+		if ((pid = fork()) == (pid_t)-1) {
+			perror("can't fork");
+			close(pipefd[0]);
+			close(pipefd[1]);
+			retransinfo = -1;
+			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;
+			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;
+		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
+