summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 18:20:54 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 18:20:54 +0000
commit7c720bec5600a9e607c875c670ca30ed351fa4ba (patch)
treec38c9bedf07616180feee6b91a1dbea038500b54 /src
parentInitial commit. (diff)
downloadiperf-7c720bec5600a9e607c875c670ca30ed351fa4ba.tar.xz
iperf-7c720bec5600a9e607c875c670ca30ed351fa4ba.zip
Adding upstream version 2.1.9+dfsg.upstream/2.1.9+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src')
-rw-r--r--src/Client.cpp1871
-rw-r--r--src/Extractor.c188
-rw-r--r--src/Launch.cpp478
-rw-r--r--src/Listener.cpp1447
-rw-r--r--src/Locale.c757
-rw-r--r--src/Makefile.am71
-rw-r--r--src/Makefile.in829
-rw-r--r--src/PerfSocket.cpp445
-rw-r--r--src/ReportOutputs.c2019
-rw-r--r--src/Reporter.c1937
-rw-r--r--src/Reports.c1091
-rw-r--r--src/Server.cpp946
-rw-r--r--src/Settings.cpp2577
-rw-r--r--src/SocketAddr.c941
-rw-r--r--src/active_hosts.cpp261
-rw-r--r--src/checkdelay.c203
-rw-r--r--src/checkisoch.cpp129
-rw-r--r--src/checkpdfs.c217
-rw-r--r--src/checksums.c229
-rw-r--r--src/dscp.c180
-rw-r--r--src/gnu_getopt.c818
-rw-r--r--src/gnu_getopt_long.c162
-rw-r--r--src/histogram.c269
-rw-r--r--src/igmp_querier.c213
-rw-r--r--src/iperf_formattime.c100
-rw-r--r--src/isochronous.cpp275
-rw-r--r--src/main.cpp479
-rw-r--r--src/packet_ring.c219
-rw-r--r--src/pdfs.c100
-rw-r--r--src/socket_io.c239
-rw-r--r--src/stdio.c355
-rw-r--r--src/tcp_window_size.c227
32 files changed, 20272 insertions, 0 deletions
diff --git a/src/Client.cpp b/src/Client.cpp
new file mode 100644
index 0000000..89e47e2
--- /dev/null
+++ b/src/Client.cpp
@@ -0,0 +1,1871 @@
+/*---------------------------------------------------------------
+ * Copyright (c) 1999,2000,2001,2002,2003
+ * The Board of Trustees of the University of Illinois
+ * All Rights Reserved.
+ *---------------------------------------------------------------
+ * 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.
+ * ________________________________________________________________
+ * National Laboratory for Applied Network Research
+ * National Center for Supercomputing Applications
+ * University of Illinois at Urbana-Champaign
+ * http://www.ncsa.uiuc.edu
+ * ________________________________________________________________
+ *
+ * Client.cpp
+ * by Mark Gates <mgates@nlanr.net>
+ * -------------------------------------------------------------------
+ * A client thread initiates a connect to the server and handles
+ * sending and receiving data, then closes the socket.
+ * ------------------------------------------------------------------- */
+#include <ctime>
+#include <cmath>
+#include "headers.h"
+#include "Client.hpp"
+#include "Thread.h"
+#include "SocketAddr.h"
+#include "PerfSocket.hpp"
+#include "Extractor.h"
+#include "delay.h"
+#include "util.h"
+#include "Locale.h"
+#include "isochronous.hpp"
+#include "pdfs.h"
+#include "version.h"
+#include "payloads.h"
+#include "active_hosts.h"
+#include "gettcpinfo.h"
+
+// const double kSecs_to_usecs = 1e6;
+const double kSecs_to_nsecs = 1e9;
+const int kBytes_to_Bits = 8;
+
+#define VARYLOAD_PERIOD 0.1 // recompute the variable load every n seconds
+#define MAXUDPBUF 1470
+
+Client::Client (thread_Settings *inSettings) {
+#ifdef HAVE_THREAD_DEBUG
+ thread_debug("Client constructor with thread %p sum=%p (flags=%x)", (void *) inSettings, (void *)inSettings->mSumReport, inSettings->flags);
+#endif
+ mSettings = inSettings;
+ myJob = NULL;
+ myReport = NULL;
+ framecounter = NULL;
+ one_report = false;
+ udp_payload_minimum = 1;
+ apply_first_udppkt_delay = false;
+
+ memset(&scratchpad, 0, sizeof(struct ReportStruct));
+ reportstruct = &scratchpad;
+ reportstruct->packetID = 1;
+ mySocket = isServerReverse(mSettings) ? mSettings->mSock : INVALID_SOCKET;
+ connected = isServerReverse(mSettings);
+ if (isCompat(mSettings) && isPeerVerDetect(mSettings)) {
+ fprintf(stderr, "%s", warn_compat_and_peer_exchange);
+ unsetPeerVerDetect(mSettings);
+ }
+
+ pattern(mSettings->mBuf, mSettings->mBufLen);
+ if (isIsochronous(mSettings)) {
+ FAIL_errno(!(mSettings->mFPS > 0.0), "Invalid value for frames per second in the isochronous settings\n", mSettings);
+ }
+ if (isFileInput(mSettings)) {
+ if (!isSTDIN(mSettings))
+ Extractor_Initialize(mSettings->mFileName, mSettings->mBufLen, mSettings);
+ else
+ Extractor_InitializeFile(stdin, mSettings->mBufLen, mSettings);
+
+ if (!Extractor_canRead(mSettings)) {
+ unsetFileInput(mSettings);
+ }
+ }
+ peerclose = false;
+ mysock_init_done = false;
+ isburst = (isIsochronous(mSettings) || isPeriodicBurst(mSettings) || (isTripTime(mSettings) && !isUDP(mSettings)));
+} // end Client
+
+/* -------------------------------------------------------------------
+ * Destructor
+ * ------------------------------------------------------------------- */
+Client::~Client () {
+#if HAVE_THREAD_DEBUG
+ thread_debug("Client destructor sock=%d report=%p server-reverse=%s fullduplex=%s", \
+ mySocket, (void *) mSettings->reporthdr, \
+ (isServerReverse(mSettings) ? "true" : "false"), (isFullDuplex(mSettings) ? "true" : "false"));
+#endif
+ DELETE_PTR(framecounter);
+} // end ~Client
+
+/* -------------------------------------------------------------------
+ * Setup a socket connected to a server.
+ * If inLocalhost is not null, bind to that address, specifying
+ * which outgoing interface to use.
+ * ------------------------------------------------------------------- */
+void Client::mySockInit (void) {
+ // create an internet socket
+ int type = (isUDP(mSettings) ? SOCK_DGRAM : SOCK_STREAM);
+ int domain = (SockAddr_isIPv6(&mSettings->peer) ?
+#if HAVE_IPV6
+ AF_INET6
+#else
+ AF_INET
+#endif
+ : AF_INET);
+
+ mySocket = socket(domain, type, 0);
+ WARN_errno(mySocket == INVALID_SOCKET, "socket");
+ // Socket is carried both by the object and the thread
+ mSettings->mSock=mySocket;
+ SetSocketOptions(mSettings);
+ SockAddr_localAddr(mSettings);
+ SockAddr_remoteAddr(mSettings);
+ if (mSettings->mLocalhost != NULL) {
+ // bind socket to local address
+ int rc = bind(mySocket, reinterpret_cast<sockaddr*>(&mSettings->local),
+ SockAddr_get_sizeof_sockaddr(&mSettings->local));
+ WARN_errno(rc == SOCKET_ERROR, "bind");
+ }
+ mysock_init_done = true;
+ if (!isUDP(mSettings) && isReport(mSettings) && isSettingsReport(mSettings)) {
+ struct ReportHeader *tmp = InitSettingsReport(mSettings);
+ assert(tmp!=NULL);
+ PostReport(tmp);
+ setNoSettReport(mSettings);
+ }
+}
+/* -------------------------------------------------------------------
+ * Setup a socket connected to a server.
+ * If inLocalhost is not null, bind to that address, specifying
+ * which outgoing interface to use.
+ * ------------------------------------------------------------------- */
+bool Client::my_connect (bool close_on_fail) {
+ int rc;
+ if (!mysock_init_done) {
+ mySockInit();
+ }
+ // connect socket
+ connected = false;
+ mSettings->tcpinitstats.connecttime = -1;
+ if (!isUDP(mSettings)) {
+ int trycnt = mSettings->mConnectRetries + 1;
+ while (trycnt > 0) {
+ connect_start.setnow();
+ rc = connect(mySocket, reinterpret_cast<sockaddr*>(&mSettings->peer),
+ SockAddr_get_sizeof_sockaddr(&mSettings->peer));
+ WARN_errno((rc == SOCKET_ERROR), "tcp connect");
+ if (rc == SOCKET_ERROR) {
+ if ((--trycnt) <= 0) {
+ if (close_on_fail) {
+ close(mySocket);
+ mySocket = INVALID_SOCKET;
+ }
+ } else {
+ delay_loop(200000);
+ }
+ } else {
+ connect_done.setnow();
+ mSettings->tcpinitstats.connecttime = 1e3 * connect_done.subSec(connect_start);
+ connected = true;
+ break;
+ }
+ }
+ } else {
+ rc = connect(mySocket, reinterpret_cast<sockaddr*>(&mSettings->peer),
+ SockAddr_get_sizeof_sockaddr(&mSettings->peer));
+ mSettings->tcpinitstats.connecttime = 0.0; // UDP doesn't have a 3WHS
+ WARN_errno((rc == SOCKET_ERROR), "udp connect");
+ if (rc != SOCKET_ERROR)
+ connected = true;
+ }
+ if (connected) {
+#if HAVE_TCP_STATS
+ assert(reportstruct);
+ if (!isUDP(mSettings)) {
+ gettcpinfo(mySocket, &mSettings->tcpinitstats);
+ }
+#endif
+ // Set the send timeout for the very first write which has the test exchange
+ int sosndtimer = TESTEXCHANGETIMEOUT; // 4 sec in usecs
+ SetSocketOptionsSendTimeout(mSettings, sosndtimer);
+ getsockname(mySocket, reinterpret_cast<sockaddr*>(&mSettings->local), &mSettings->size_local);
+ getpeername(mySocket, reinterpret_cast<sockaddr*>(&mSettings->peer), &mSettings->size_peer);
+ SockAddr_Ifrname(mSettings);
+#ifdef DEFAULT_PAYLOAD_LEN_PER_MTU_DISCOVERY
+ if (isUDP(mSettings) && !isBuflenSet(mSettings)) {
+ checksock_max_udp_payload(mSettings);
+ }
+#endif
+ if (isUDP(mSettings) && !isIsochronous(mSettings) && !isIPG(mSettings)) {
+ mSettings->mBurstIPG = get_delay_target() / 1e3; // this is being set for the settings report only
+ }
+ if (isReport(mSettings) && isSettingsReport(mSettings)) {
+ struct ReportHeader *tmp = InitSettingsReport(mSettings);
+ assert(tmp!=NULL);
+ PostReport(tmp);
+ setNoSettReport(mSettings);
+ }
+ } else {
+ if (mySocket != INVALID_SOCKET) {
+ int rc = close(mySocket);
+ WARN_errno(rc == SOCKET_ERROR, "client connect close");
+ mySocket = INVALID_SOCKET;
+ }
+ }
+ // Post the connect report unless peer version exchange is set
+ if (isConnectionReport(mSettings) && !isSumOnly(mSettings)) {
+ if (connected) {
+ struct ReportHeader *reporthdr = InitConnectionReport(mSettings);
+ struct ConnectionInfo *cr = static_cast<struct ConnectionInfo *>(reporthdr->this_report);
+ cr->connect_timestamp.tv_sec = connect_start.getSecs();
+ cr->connect_timestamp.tv_usec = connect_start.getUsecs();
+ assert(reporthdr);
+ PostReport(reporthdr);
+ } else {
+ PostReport(InitConnectionReport(mSettings));
+ }
+ }
+ return connected;
+} // end Connect
+
+bool Client::isConnected () const {
+#ifdef HAVE_THREAD_DEBUG
+ // thread_debug("Client is connected %d", connected);
+#endif
+ return connected;
+}
+
+void Client::TxDelay () {
+ if (isTxHoldback(mSettings)) {
+ clock_usleep(&mSettings->txholdback_timer);
+ }
+}
+
+inline void Client::myReportPacket (void) {
+ ReportPacket(myReport, reportstruct);
+ reportstruct->packetLen = 0;
+}
+
+
+// There are multiple startup synchronizations, this code
+// handles them all. The caller decides to apply them
+// either before connect() or after connect() and before writes()
+int Client::StartSynch () {
+#ifdef HAVE_THREAD_DEBUG
+ thread_debug("Client start sync enterred");
+#endif
+ bool delay_test_exchange = (isFullDuplex(mSettings) && isUDP(mSettings));
+ if (isTxStartTime(mSettings) && delay_test_exchange) {
+ clock_usleep_abstime(&mSettings->txstart_epoch);
+ }
+ myJob = InitIndividualReport(mSettings);
+ myReport = static_cast<struct ReporterData *>(myJob->this_report);
+ myReport->info.common->socket=mySocket;
+ myReport->info.isEnableTcpInfo = false; // default here, set in init traffic actions
+ if (!isReverse(mSettings) && (mSettings->mReportMode == kReport_CSV)) {
+ format_ips_port_string(&myReport->info, 0);
+ }
+
+ // Perform delays, usually between connect() and data xfer though before connect
+ // Two delays are supported:
+ // o First is an absolute start time per unix epoch format
+ // o Second is a holdback, a relative amount of seconds between the connect and data xfers
+ // check for an epoch based start time
+ reportstruct->packetLen = 0;
+ if (!isServerReverse(mSettings)) {
+ if (!isCompat(mSettings) && !isBounceBack(mSettings)) {
+ reportstruct->packetLen = SendFirstPayload();
+ // Reverse UDP tests need to retry "first sends" a few times
+ // before going to server or read mode
+ if (isReverse(mSettings) && isUDP(mSettings)) {
+ reportstruct->packetLen = 0;
+ fd_set set;
+ struct timeval timeout;
+ int resend_udp = 100;
+ while (--resend_udp > 0) {
+ FD_ZERO(&set);
+ FD_SET(mySocket, &set);
+ timeout.tv_sec = 0;
+ timeout.tv_usec = rand() % 20000; // randomize IPG a bit
+ if (select(mySocket + 1, &set, NULL, NULL, &timeout) == 0) {
+ reportstruct->packetLen = SendFirstPayload();
+ // printf("**** resend sock=%d count=%d\n", mySocket, resend_udp);
+ } else {
+ break;
+ }
+ }
+ }
+ }
+ if (isTxStartTime(mSettings) && !delay_test_exchange) {
+ clock_usleep_abstime(&mSettings->txstart_epoch);
+ } else if (isTxHoldback(mSettings)) {
+ TxDelay();
+ }
+ // Server side client
+ } else if (isTripTime(mSettings) || isPeriodicBurst(mSettings)) {
+ reportstruct->packetLen = SendFirstPayload();
+ }
+ if (isIsochronous(mSettings) || isPeriodicBurst(mSettings)) {
+ Timestamp tmp;
+ tmp.set(mSettings->txstart_epoch.tv_sec, mSettings->txstart_epoch.tv_usec);
+ framecounter = new Isochronous::FrameCounter(mSettings->mFPS, tmp);
+ // set the mbuf valid for burst period ahead of time. The same value will be set for all burst writes
+ if (!isUDP(mSettings) && framecounter) {
+ struct TCP_burst_payload * mBuf_burst = reinterpret_cast<struct TCP_burst_payload *>(mSettings->mBuf);
+ mBuf_burst->burst_period_us = htonl(framecounter->period_us());
+ }
+ }
+ int setfullduplexflag = 0;
+ if (isFullDuplex(mSettings) && !isServerReverse(mSettings)) {
+ assert(mSettings->mFullDuplexReport != NULL);
+ if ((setfullduplexflag = fullduplex_start_barrier(&mSettings->mFullDuplexReport->fullduplex_barrier)) < 0)
+ return -1;
+ }
+ SetReportStartTime();
+#if HAVE_TCP_STATS
+ if (!isUDP(mSettings)) {
+ // Near congestion and peridiodic need sampling on every report packet
+ if (isNearCongest(mSettings) || isPeriodicBurst(mSettings)) {
+ myReport->info.isEnableTcpInfo = true;
+ myReport->info.ts.nextTCPSampleTime.tv_sec = 0;
+ myReport->info.ts.nextTCPSampleTime.tv_usec = 0;
+ } else if (isEnhanced(mSettings) || isBounceBack(mSettings)) {
+ myReport->info.isEnableTcpInfo = true;
+ myReport->info.ts.nextTCPSampleTime = myReport->info.ts.nextTime;
+ }
+ }
+#endif
+
+ if (reportstruct->packetLen > 0) {
+ reportstruct->packetTime = myReport->info.ts.startTime;
+ reportstruct->sentTime = reportstruct->packetTime;
+ reportstruct->prevSentTime = reportstruct->packetTime;
+ reportstruct->prevPacketTime = myReport->info.ts.prevpacketTime;
+ if (isModeAmount(mSettings)) {
+ mSettings->mAmount -= reportstruct->packetLen;
+ }
+ myReportPacket();
+ myReport->info.ts.prevpacketTime = reportstruct->packetTime;
+ reportstruct->packetID++;
+ }
+ if (setfullduplexflag) {
+ SetFullDuplexReportStartTime();
+ }
+ // Full duplex sockets need to be syncronized
+#ifdef HAVE_THREAD_DEBUG
+ thread_debug("Client start sync exited");
+#endif
+ return 0;
+}
+
+inline void Client::SetFullDuplexReportStartTime () {
+ assert(myReport->FullDuplexReport != NULL);
+ struct TransferInfo *fullduplexstats = &myReport->FullDuplexReport->info;
+ assert(fullduplexstats != NULL);
+ if (TimeZero(fullduplexstats->ts.startTime)) {
+ fullduplexstats->ts.startTime = myReport->info.ts.startTime;
+ if (isModeTime(mSettings)) {
+ fullduplexstats->ts.nextTime = myReport->info.ts.nextTime;
+ }
+ }
+#ifdef HAVE_THREAD_DEBUG
+ thread_debug("Client fullduplex report start=%ld.%ld next=%ld.%ld", fullduplexstats->ts.startTime.tv_sec, fullduplexstats->ts.startTime.tv_usec, fullduplexstats->ts.nextTime.tv_sec, fullduplexstats->ts.nextTime.tv_usec);
+#endif
+}
+inline void Client::SetReportStartTime () {
+ assert(myReport!=NULL);
+ now.setnow();
+ myReport->info.ts.startTime.tv_sec = now.getSecs();
+ myReport->info.ts.startTime.tv_usec = now.getUsecs();
+ myReport->info.ts.IPGstart = myReport->info.ts.startTime;
+ myReport->info.ts.prevpacketTime = myReport->info.ts.startTime;
+ if (!TimeZero(myReport->info.ts.intervalTime)) {
+ myReport->info.ts.nextTime = myReport->info.ts.startTime;
+ TimeAdd(myReport->info.ts.nextTime, myReport->info.ts.intervalTime);
+ }
+ if (myReport->GroupSumReport) {
+ struct TransferInfo *sumstats = &myReport->GroupSumReport->info;
+ assert(sumstats != NULL);
+ Mutex_Lock(&myReport->GroupSumReport->reference.lock);
+ if (TimeZero(sumstats->ts.startTime)) {
+ sumstats->ts.startTime = myReport->info.ts.startTime;
+ if (isModeTime(mSettings) || isModeInfinite(mSettings)) {
+ sumstats->ts.nextTime = myReport->info.ts.nextTime;
+ }
+#ifdef HAVE_THREAD_DEBUG
+ thread_debug("Client group sum report start=%ld.%ld next=%ld.%ld", sumstats->ts.startTime.tv_sec, sumstats->ts.startTime.tv_usec, sumstats->ts.nextTime.tv_sec, sumstats->ts.nextTime.tv_usec);
+#endif
+ }
+ Mutex_Unlock(&myReport->GroupSumReport->reference.lock);
+ }
+#ifdef HAVE_THREAD_DEBUG
+ thread_debug("Client(%d) report start/ipg=%ld.%ld next=%ld.%ld", mSettings->mSock, myReport->info.ts.startTime.tv_sec, myReport->info.ts.startTime.tv_usec, myReport->info.ts.nextTime.tv_sec, myReport->info.ts.nextTime.tv_usec);
+#endif
+}
+
+void Client::ConnectPeriodic () {
+ Timestamp end;
+ Timestamp next;
+ unsigned int amount_usec = 1000000;
+ if (isModeTime(mSettings)) {
+ amount_usec = (mSettings->mAmount * 10000);
+ end.add(amount_usec); // add in micro seconds
+ }
+ setNoConnectSync(mSettings);
+ int num_connects = -1;
+ if (!(mSettings->mInterval > 0)) {
+ if (mSettings->connectonly_count < 0)
+ num_connects = 10;
+ else if (mSettings->connectonly_count > 0)
+ num_connects = mSettings->connectonly_count;
+ }
+
+ do {
+ if (my_connect(false)){
+ int rc = close(mySocket);
+ WARN_errno(rc == SOCKET_ERROR, "client close");
+ mySocket = INVALID_SOCKET;
+ mysock_init_done = false;
+ }
+ if (mSettings->mInterval > 0) {
+ now.setnow();
+ do {
+ next.add(mSettings->mInterval);
+ } while (next.before(now));
+ if (next.before(end)) {
+ struct timeval tmp;
+ tmp.tv_sec = next.getSecs();
+ tmp.tv_usec = next.getUsecs();
+ clock_usleep_abstime(&tmp);
+ }
+ }
+ if (num_connects > 0) {
+ --num_connects;
+ }
+ } while (num_connects && !sInterupted && (next.before(end) || (isModeTime(mSettings) && !(mSettings->mInterval > 0))));
+}
+/* -------------------------------------------------------------------
+ * Common traffic loop intializations
+ * ------------------------------------------------------------------- */
+void Client::InitTrafficLoop () {
+ // Enable socket write timeouts for responsive reporting
+ // Do this after the connection establishment
+ // and after Client::InitiateServer as during these
+ // default socket timeouts are preferred.
+ int sosndtimer = 0;
+ // sosndtimer units microseconds
+ // mInterval units are microseconds, mAmount units is 10 ms
+ // SetSocketOptionsSendTimeout takes microseconds
+ // Set the timeout value to 1/2 the interval (per -i) or 1/2 the -t value
+ if (isPeriodicBurst(mSettings) && (mSettings->mFPS > 0.0)) {
+ sosndtimer = static_cast<int>(round(250000.0 / mSettings->mFPS));
+ } else if (mSettings->mInterval > 0) {
+ sosndtimer = static_cast<int>(round(0.5 * mSettings->mInterval));
+ } else {
+ sosndtimer = static_cast<int>(mSettings->mAmount * 5e3);
+ }
+ SetSocketOptionsSendTimeout(mSettings, sosndtimer);
+ // set the lower bounds delay based of the socket timeout timer
+ // units needs to be in nanoseconds
+ delay_lower_bounds = static_cast<double>(sosndtimer) * -1e3;
+
+ if (isIsochronous(mSettings))
+ myReport->info.matchframeID = 1;
+
+ // set the total bytes sent to zero
+ totLen = 0;
+ if (isModeTime(mSettings)) {
+ mEndTime.setnow();
+ mEndTime.add(mSettings->mAmount / 100.0);
+ // now.setnow(); fprintf(stderr, "DEBUG: end time set to %ld.%ld now is %ld.%ld\n", mEndTime.getSecs(), mEndTime.getUsecs(), now.getSecs(), now.getUsecs());
+ }
+ readAt = mSettings->mBuf;
+ lastPacketTime.set(myReport->info.ts.startTime.tv_sec, myReport->info.ts.startTime.tv_usec);
+ reportstruct->errwrite=WriteNoErr;
+ reportstruct->emptyreport=0;
+ reportstruct->packetLen = 0;
+ // Finally, post this thread's "job report" which the reporter thread
+ // will continuously process as long as there are packets flowing
+ // right now the ring is empty
+ if (!isReverse(mSettings) && !isSingleUDP(mSettings) && isDataReport(mSettings)) {
+ assert(myJob!=NULL);
+ assert(myReport!=NULL);
+ PostReport(myJob);
+ }
+ one_report = (!isUDP(mSettings) && !isEnhanced(mSettings) && (mSettings->mIntervalMode != kInterval_Time) \
+ && !isIsochronous(mSettings) && !isPeriodicBurst(mSettings) && !isTripTime(mSettings) && !isReverse(mSettings));
+}
+
+/* -------------------------------------------------------------------
+ * Run the appropriate send loop between
+ *
+ * 1) TCP without rate limiting
+ * 2) TCP with rate limiting
+ * 3) UDP
+ * 4) UDP isochronous w/vbr
+ *
+ * ------------------------------------------------------------------- */
+void Client::Run () {
+ // Initialize the report struct scratch pad
+ // Peform common traffic setup
+ InitTrafficLoop();
+ /*
+ * UDP
+ */
+ if (isUDP(mSettings)) {
+ if (isFileInput(mSettings)) {
+ // Due to the UDP timestamps etc, included
+ // reduce the read size by an amount
+ // equal to the header size
+ Extractor_reduceReadSize(sizeof(struct UDP_datagram), mSettings);
+ readAt += sizeof(struct UDP_datagram);
+ }
+ // Launch the approprate UDP traffic loop
+ if (isIsochronous(mSettings)) {
+ RunUDPIsochronous();
+ } else {
+ RunUDP();
+ }
+ } else {
+ // Launch the approprate TCP traffic loop
+ if (isBounceBack(mSettings)) {
+ RunBounceBackTCP();
+ } else if (mSettings->mAppRate > 0) {
+ RunRateLimitedTCP();
+ } else if (isNearCongest(mSettings)) {
+ RunNearCongestionTCP();
+#if HAVE_DECL_TCP_NOTSENT_LOWAT
+ } else if (isWritePrefetch(mSettings) && \
+ !isIsochronous(mSettings) && !isPeriodicBurst(mSettings)) {
+ RunWriteEventsTCP();
+#endif
+ } else {
+ RunTCP();
+ }
+ }
+}
+
+/*
+ * TCP send loop
+ */
+void Client::RunTCP () {
+ int burst_remaining = 0;
+ uint32_t burst_id = 1;
+ int writelen = mSettings->mBufLen;
+
+ now.setnow();
+ reportstruct->packetTime.tv_sec = now.getSecs();
+ reportstruct->packetTime.tv_usec = now.getUsecs();
+ reportstruct->write_time = 0;
+ while (InProgress()) {
+ reportstruct->writecnt = 0;
+ reportstruct->scheduled = false;
+ if (isModeAmount(mSettings)) {
+ writelen = ((mSettings->mAmount < static_cast<unsigned>(mSettings->mBufLen)) ? mSettings->mAmount : mSettings->mBufLen);
+ }
+ if (isburst && !(burst_remaining > 0)) {
+ if (isIsochronous(mSettings)) {
+ assert(mSettings->mMean);
+ burst_remaining = static_cast<int>(lognormal(mSettings->mMean,mSettings->mVariance)) / (mSettings->mFPS * 8);
+ } else if (isPeriodicBurst(mSettings)){
+ assert(mSettings->mBurstSize);
+ burst_remaining = mSettings->mBurstSize;
+ } else {
+ burst_remaining = mSettings->mBufLen;
+ }
+ // check for TCP minimum payload
+ if (burst_remaining < static_cast<int>(sizeof(struct TCP_burst_payload)))
+ burst_remaining = static_cast<int>(sizeof(struct TCP_burst_payload));
+ // apply scheduling if needed
+ if (framecounter) {
+ burst_id = framecounter->wait_tick(&reportstruct->sched_err);
+ reportstruct->scheduled = true;
+ if (isPeriodicBurst(mSettings)) {
+ // low duty cycle traffic needs special event handling
+ now.setnow();
+ myReport->info.ts.prevsendTime = reportstruct->packetTime;
+ reportstruct->packetTime.tv_sec = now.getSecs();
+ reportstruct->packetTime.tv_usec = now.getUsecs();
+ if (!InProgress()) {
+ reportstruct->packetLen = 0;
+ reportstruct->emptyreport = 1;
+ // wait may have crossed the termination boundry
+ break;
+ } else {
+ //time interval crossings may have occurred during the wait
+ //post a null event to cause the report to flush the packet ring
+ PostNullEvent();
+ }
+ }
+#if HAVE_DECL_TCP_NOTSENT_LOWAT
+ if (isWritePrefetch(mSettings)) {
+ AwaitWriteSelectEventTCP();
+ }
+#endif
+ }
+ now.setnow();
+ reportstruct->packetTime.tv_sec = now.getSecs();
+ reportstruct->packetTime.tv_usec = now.getUsecs();
+ WriteTcpTxHdr(reportstruct, burst_remaining, burst_id++);
+ reportstruct->sentTime = reportstruct->packetTime;
+ myReport->info.ts.prevsendTime = reportstruct->packetTime;
+ writelen = (mSettings->mBufLen > burst_remaining) ? burst_remaining : mSettings->mBufLen;
+ // perform write, full header must succeed
+ if (isTcpWriteTimes(mSettings)) {
+ write_start.setnow();
+ }
+ reportstruct->packetLen = writen(mySocket, mSettings->mBuf, writelen, &reportstruct->writecnt);
+ FAIL_errno(reportstruct->packetLen < (intmax_t) sizeof(struct TCP_burst_payload), "burst written", mSettings);
+ if (isTcpWriteTimes(mSettings)) {
+ now.setnow();
+ reportstruct->write_time = now.subUsec(write_start);
+ }
+ } else {
+ // printf("pl=%ld\n",reportstruct->packetLen);
+ // perform write
+ if (isburst)
+ writelen = (mSettings->mBufLen > burst_remaining) ? burst_remaining : mSettings->mBufLen;
+ if (isTcpWriteTimes(mSettings)) {
+ write_start.setnow();
+ }
+#if HAVE_DECL_TCP_NOTSENT_LOWAT
+ if (isWritePrefetch(mSettings)) {
+ AwaitWriteSelectEventTCP();
+ }
+#endif
+ reportstruct->packetLen = write(mySocket, mSettings->mBuf, writelen);
+ now.setnow();
+ reportstruct->writecnt++;
+ reportstruct->packetTime.tv_sec = now.getSecs();
+ reportstruct->packetTime.tv_usec = now.getUsecs();
+ if (isTcpWriteTimes(mSettings)) {
+ reportstruct->write_time = now.subUsec(write_start);
+ }
+ reportstruct->sentTime = reportstruct->packetTime;
+ }
+ if (reportstruct->packetLen <= 0) {
+ if (reportstruct->packetLen == 0) {
+ peerclose = true;
+ } else if (NONFATALTCPWRITERR(errno)) {
+ reportstruct->errwrite=WriteErrAccount;
+ } else if (FATALTCPWRITERR(errno)) {
+ reportstruct->errwrite=WriteErrFatal;
+ WARN_errno(1, "tcp write");
+ break;
+ } else {
+ reportstruct->errwrite=WriteErrNoAccount;
+ }
+ reportstruct->packetLen = 0;
+ reportstruct->emptyreport = 1;
+ } else {
+ reportstruct->emptyreport = 0;
+ totLen += reportstruct->packetLen;
+ reportstruct->errwrite=WriteNoErr;
+ if (isburst) {
+ burst_remaining -= reportstruct->packetLen;
+ if (burst_remaining > 0) {
+ reportstruct->transit_ready = 0;
+ } else {
+ reportstruct->transit_ready = 1;
+ reportstruct->prevSentTime = myReport->info.ts.prevsendTime;
+ }
+ }
+ }
+ if (isModeAmount(mSettings) && !reportstruct->emptyreport) {
+ /* mAmount may be unsigned, so don't let it underflow! */
+ if (mSettings->mAmount >= static_cast<unsigned long>(reportstruct->packetLen)) {
+ mSettings->mAmount -= static_cast<unsigned long>(reportstruct->packetLen);
+ } else {
+ mSettings->mAmount = 0;
+ }
+ }
+ if (!one_report) {
+ myReportPacket();
+ }
+ }
+ FinishTrafficActions();
+}
+
+/*
+ * TCP send loop
+ */
+void Client::RunNearCongestionTCP () {
+ int burst_remaining = 0;
+ int burst_id = 1;
+ now.setnow();
+ reportstruct->packetTime.tv_sec = now.getSecs();
+ reportstruct->packetTime.tv_usec = now.getUsecs();
+ while (InProgress()) {
+ reportstruct->writecnt = 0;
+ if (isModeAmount(mSettings)) {
+ reportstruct->packetLen = ((mSettings->mAmount < static_cast<unsigned>(mSettings->mBufLen)) ? mSettings->mAmount : mSettings->mBufLen);
+ } else {
+ reportstruct->packetLen = mSettings->mBufLen;
+ }
+ if (!burst_remaining) {
+ burst_remaining = mSettings->mBufLen;
+ // mAmount check
+ now.setnow();
+ reportstruct->packetTime.tv_sec = now.getSecs();
+ reportstruct->packetTime.tv_usec = now.getUsecs();
+ WriteTcpTxHdr(reportstruct, burst_remaining, burst_id++);
+ reportstruct->sentTime = reportstruct->packetTime;
+ myReport->info.ts.prevsendTime = reportstruct->packetTime;
+ // perform write
+ int writelen = (mSettings->mBufLen > burst_remaining) ? burst_remaining : mSettings->mBufLen;
+ reportstruct->packetLen = write(mySocket, mSettings->mBuf, writelen);
+ reportstruct->writecnt++;
+ assert(reportstruct->packetLen >= (intmax_t) sizeof(struct TCP_burst_payload));
+ goto ReportNow;
+ }
+ if (reportstruct->packetLen > burst_remaining) {
+ reportstruct->packetLen = burst_remaining;
+ }
+ // printf("pl=%ld\n",reportstruct->packetLen);
+ // perform write
+ reportstruct->packetLen = write(mySocket, mSettings->mBuf, reportstruct->packetLen);
+ now.setnow();
+ reportstruct->writecnt++;
+ reportstruct->packetTime.tv_sec = now.getSecs();
+ reportstruct->packetTime.tv_usec = now.getUsecs();
+ reportstruct->sentTime = reportstruct->packetTime;
+ ReportNow:
+ reportstruct->transit_ready = 0;
+ if (reportstruct->packetLen < 0) {
+ if (NONFATALTCPWRITERR(errno)) {
+ reportstruct->errwrite=WriteErrAccount;
+ } else if (FATALTCPWRITERR(errno)) {
+ reportstruct->errwrite=WriteErrFatal;
+ WARN_errno(1, "tcp write");
+ break;
+ } else {
+ reportstruct->errwrite=WriteErrNoAccount;
+ }
+ reportstruct->packetLen = 0;
+ reportstruct->emptyreport = 1;
+ } else {
+ reportstruct->emptyreport = 0;
+ totLen += reportstruct->packetLen;
+ reportstruct->errwrite=WriteNoErr;
+ burst_remaining -= reportstruct->packetLen;
+ if (burst_remaining <= 0) {
+ reportstruct->transit_ready = 1;
+ }
+ }
+ if (isModeAmount(mSettings) && !reportstruct->emptyreport) {
+ /* mAmount may be unsigned, so don't let it underflow! */
+ if (mSettings->mAmount >= static_cast<unsigned long>(reportstruct->packetLen)) {
+ mSettings->mAmount -= static_cast<unsigned long>(reportstruct->packetLen);
+ } else {
+ mSettings->mAmount = 0;
+ }
+ }
+ // apply placing after write burst completes
+ if (reportstruct->transit_ready) {
+ myReportPacket(); // this will set the tcpstats in the report struct
+ // pacing timer is weighted by the RTT (set to 1 when RTT is not supported)
+ int pacing_timer = 0;
+#if HAVE_TCP_STATS
+ pacing_timer = static_cast<int>(std::ceil(static_cast<double>(reportstruct->tcpstats.rtt) * mSettings->rtt_nearcongest_weight_factor));
+#else
+ pacing_timer = static_cast<int>(100 * mSettings->rtt_nearcongest_weight_factor);
+#endif
+ if (pacing_timer)
+ delay_loop(pacing_timer);
+ }
+ }
+ FinishTrafficActions();
+}
+
+/*
+ * A version of the transmit loop that supports TCP rate limiting using a token bucket
+ */
+void Client::RunRateLimitedTCP () {
+ double tokens = 0;
+ Timestamp time1, time2;
+ int burst_size = mSettings->mBufLen;
+ int burst_remaining = 0;
+ int burst_id = 1;
+
+ long var_rate = mSettings->mAppRate;
+ int fatalwrite_err = 0;
+
+ now.setnow();
+ reportstruct->packetTime.tv_sec = now.getSecs();
+ reportstruct->packetTime.tv_usec = now.getUsecs();
+ while (InProgress() && !fatalwrite_err) {
+ reportstruct->writecnt = 0;
+ // Add tokens per the loop time
+ time2.setnow();
+ if (isVaryLoad(mSettings)) {
+ static Timestamp time3;
+ if (time2.subSec(time3) >= VARYLOAD_PERIOD) {
+ var_rate = lognormal(mSettings->mAppRate,mSettings->mVariance);
+ time3 = time2;
+ if (var_rate < 0)
+ var_rate = 0;
+ }
+ }
+ tokens += time2.subSec(time1) * (var_rate / 8.0);
+ time1 = time2;
+ if (tokens >= 0.0) {
+ if (isModeAmount(mSettings)) {
+ reportstruct->packetLen = ((mSettings->mAmount < static_cast<unsigned>(mSettings->mBufLen)) ? mSettings->mAmount : mSettings->mBufLen);
+ } else {
+ reportstruct->packetLen = mSettings->mBufLen;
+ }
+ // perform write
+ int len = 0;
+ int len2 = 0;
+ if (burst_remaining == 0) {
+ burst_remaining = mSettings->mBufLen;
+ now.setnow();
+ reportstruct->packetTime.tv_sec = now.getSecs();
+ reportstruct->packetTime.tv_usec = now.getUsecs();
+ reportstruct->sentTime = reportstruct->packetTime;
+ WriteTcpTxHdr(reportstruct, burst_size, burst_id++);
+ // perform write
+ if (isTripTime(mSettings)) {
+ len = writen(mySocket, mSettings->mBuf, mSettings->mBufLen, &reportstruct->writecnt);
+ WARN(len != mSettings->mBufLen, "burst write failed");
+ } else {
+ len = writen(mySocket, mSettings->mBuf, sizeof(struct TCP_burst_payload), &reportstruct->writecnt);
+ WARN(len != sizeof(struct TCP_burst_payload), "burst hdr write failed");
+ }
+ if (len < 0) {
+ len = 0;
+ if (NONFATALTCPWRITERR(errno)) {
+ reportstruct->errwrite=WriteErrAccount;
+ } else if (FATALTCPWRITERR(errno)) {
+ reportstruct->errwrite=WriteErrFatal;
+ WARN_errno(1, "write");
+ fatalwrite_err = 1;
+ break;
+ } else {
+ reportstruct->errwrite=WriteErrNoAccount;
+ }
+ } else {
+ burst_remaining -= len;
+ }
+ // thread_debug("***write burst header %d id=%d", burst_size, (burst_id - 1));
+ } else if (reportstruct->packetLen > burst_remaining) {
+ reportstruct->packetLen = burst_remaining;
+ }
+ if (burst_remaining > 0) {
+ len2 = write(mySocket, mSettings->mBuf, reportstruct->packetLen);
+ reportstruct->writecnt++;
+ }
+ if (len2 < 0) {
+ len2 = 0;
+ if (NONFATALTCPWRITERR(errno)) {
+ reportstruct->errwrite=WriteErrAccount;
+ } else if (FATALTCPWRITERR(errno)) {
+ reportstruct->errwrite=WriteErrFatal;
+ WARN_errno(1, "write");
+ fatalwrite_err = 1;
+ break;
+ } else {
+ reportstruct->errwrite=WriteErrNoAccount;
+ }
+ } else {
+ // Consume tokens per the transmit
+ tokens -= (len + len2);
+ totLen += (len + len2);;
+ reportstruct->errwrite=WriteNoErr;
+ }
+ time2.setnow();
+ reportstruct->packetLen = len + len2;
+ reportstruct->packetTime.tv_sec = time2.getSecs();
+ reportstruct->packetTime.tv_usec = time2.getUsecs();
+ reportstruct->sentTime = reportstruct->packetTime;
+ if (isModeAmount(mSettings)) {
+ /* mAmount may be unsigned, so don't let it underflow! */
+ if (mSettings->mAmount >= static_cast<unsigned long>(reportstruct->packetLen)) {
+ mSettings->mAmount -= static_cast<unsigned long>(reportstruct->packetLen);
+ } else {
+ mSettings->mAmount = 0;
+ }
+ }
+ if (!one_report) {
+ myReportPacket();
+ }
+ } else {
+ // Use a 4 usec delay to fill tokens
+#if HAVE_DECL_TCP_NOTSENT_LOWAT
+ if (isWritePrefetch(mSettings)) {
+ AwaitWriteSelectEventTCP();
+ } else
+#endif
+ {
+ delay_loop(4);
+ }
+ }
+ }
+ FinishTrafficActions();
+}
+
+#if HAVE_DECL_TCP_NOTSENT_LOWAT
+inline bool Client::AwaitWriteSelectEventTCP (void) {
+ int rc;
+ struct timeval timeout;
+ fd_set writeset;
+ FD_ZERO(&writeset);
+ FD_SET(mySocket, &writeset);
+ if (isModeTime(mSettings)) {
+ Timestamp write_event_timeout(0,0);
+ if (mSettings->mInterval && (mSettings->mIntervalMode == kInterval_Time)) {
+ write_event_timeout.add((double) mSettings->mInterval / 1e6 * 2.0);
+ } else {
+ write_event_timeout.add((double) mSettings->mAmount / 1e2 * 4.0);
+ }
+ timeout.tv_sec = write_event_timeout.getSecs();
+ timeout.tv_usec = write_event_timeout.getUsecs();
+ } else {
+ timeout.tv_sec = 10; // longest is 10 seconds
+ timeout.tv_usec = 0;
+ }
+
+ if ((rc = select(mySocket + 1, NULL, &writeset, NULL, &timeout)) <= 0) {
+ WARN_errno((rc < 0), "select");
+#ifdef HAVE_THREAD_DEBUG
+ if (rc == 0)
+ thread_debug("AwaitWrite timeout");
+#endif
+ return false;
+ }
+ return true;
+}
+
+void Client::RunWriteEventsTCP () {
+ int burst_id = 0;
+ int writelen = mSettings->mBufLen;
+ Timestamp write_end;
+
+ now.setnow();
+ reportstruct->packetTime.tv_sec = now.getSecs();
+ reportstruct->packetTime.tv_usec = now.getUsecs();
+ while (InProgress()) {
+ if (isModeAmount(mSettings)) {
+ writelen = ((mSettings->mAmount < static_cast<unsigned>(mSettings->mBufLen)) ? mSettings->mAmount : mSettings->mBufLen);
+ }
+ now.setnow();
+ reportstruct->write_time = 0;
+ if (isTcpWriteTimes(mSettings)) {
+ write_start = now;
+ }
+ bool rc = AwaitWriteSelectEventTCP();
+ reportstruct->emptyreport = (rc == false) ? 1 : 0;
+ if (rc) {
+ now.setnow();
+ reportstruct->packetTime.tv_sec = now.getSecs();
+ reportstruct->packetTime.tv_usec = now.getUsecs();
+ WriteTcpTxHdr(reportstruct, writelen, ++burst_id);
+ reportstruct->sentTime = reportstruct->packetTime;
+ reportstruct->packetLen = writen(mySocket, mSettings->mBuf, writelen, &reportstruct->writecnt);
+ if (reportstruct->packetLen <= 0) {
+ WARN_errno((reportstruct->packetLen < 0), "event writen()");
+ if (reportstruct->packetLen == 0) {
+ peerclose = true;
+ }
+ reportstruct->packetLen = 0;
+ reportstruct->emptyreport = 1;
+ } else if (isTcpWriteTimes(mSettings)) {
+ write_end.setnow();
+ reportstruct->write_time = write_end.subUsec(write_start);
+ }
+ }
+ if (isModeAmount(mSettings) && !reportstruct->emptyreport) {
+ /* mAmount may be unsigned, so don't let it underflow! */
+ if (mSettings->mAmount >= static_cast<unsigned long>(reportstruct->packetLen)) {
+ mSettings->mAmount -= static_cast<unsigned long>(reportstruct->packetLen);
+ } else {
+ mSettings->mAmount = 0;
+ }
+ }
+ if (!one_report) {
+ myReportPacket();
+ }
+ }
+ FinishTrafficActions();
+}
+#endif
+void Client::RunBounceBackTCP () {
+ int burst_id = 0;
+ int writelen = mSettings->mBufLen;
+ memset(mSettings->mBuf, 0x5A, sizeof(struct bounceback_hdr));
+ if (mSettings->mInterval && (mSettings->mIntervalMode == kInterval_Time)) {
+ int sotimer = static_cast<int>(round(mSettings->mInterval / 2.0));
+ SetSocketOptionsReceiveTimeout(mSettings, sotimer);
+ SetSocketOptionsSendTimeout(mSettings, sotimer);
+ } else if (isModeTime(mSettings)) {
+ int sotimer = static_cast<int>(round(mSettings->mAmount * 10000) / 2);
+ SetSocketOptionsReceiveTimeout(mSettings, sotimer);
+ SetSocketOptionsSendTimeout(mSettings, sotimer);
+ }
+ if (isModeTime(mSettings)) {
+ uintmax_t end_usecs = (mSettings->mAmount * 10000);
+ if (int err = set_itimer(end_usecs)) {
+ FAIL_errno(err != 0, "setitimer", mSettings);
+ }
+ }
+ now.setnow();
+ reportstruct->packetTime.tv_sec = now.getSecs();
+ reportstruct->packetTime.tv_usec = now.getUsecs();
+ while (InProgress()) {
+ int n;
+ reportstruct->writecnt = 0;
+ bool isFirst;
+ if (framecounter) {
+ burst_id = framecounter->wait_tick(&reportstruct->sched_err);
+ PostNullEvent(true); // this will set the now timestamp
+ reportstruct->sentTime.tv_sec = now.getSecs();
+ reportstruct->sentTime.tv_usec = now.getUsecs();
+ isFirst = true;
+ } else {
+ burst_id++;
+ isFirst = false;
+ }
+ int bb_burst = (mSettings->mBounceBackBurst > 0) ? mSettings->mBounceBackBurst : 1;
+ while (bb_burst > 0) {
+ bb_burst--;
+ if (isFirst) {
+ isFirst = false;
+ } else {
+ now.setnow();
+ reportstruct->sentTime.tv_sec = now.getSecs();
+ reportstruct->sentTime.tv_usec = now.getUsecs();
+ }
+ WriteTcpTxBBHdr(reportstruct, burst_id, 0);
+ reportstruct->packetLen = writen(mySocket, mSettings->mBuf, writelen, &reportstruct->writecnt);
+ if (reportstruct->packetLen <= 0) {
+ if ((reportstruct->packetLen < 0) && FATALTCPWRITERR(errno)) {
+ reportstruct->errwrite=WriteErrFatal;
+ WARN_errno(1, "tcp bounceback write fatal error");
+ peerclose = true;
+ } else if (reportstruct->packetLen == 0) {
+ peerclose = true;
+ } else if (reportstruct->packetLen != writelen) {
+ WARN_errno(1, "tcp bounceback writen incomplete");
+ peerclose = true;
+ } else {
+ // retry the write
+ bb_burst++;
+ continue;
+ }
+ break;
+ }
+ if (reportstruct->packetLen == writelen) {
+ reportstruct->emptyreport = 0;
+ totLen += reportstruct->packetLen;
+ reportstruct->errwrite=WriteNoErr;
+#if HAVE_DECL_TCP_QUICKACK
+ if (isTcpQuickAck(mSettings)) {
+ int opt = 1;
+ Socklen_t len = sizeof(opt);
+ int rc = setsockopt(mySocket, IPPROTO_TCP, TCP_QUICKACK,
+ reinterpret_cast<char*>(&opt), len);
+ WARN_errno(rc == SOCKET_ERROR, "setsockopt TCP_QUICKACK");
+ }
+#endif
+ if ((n = recvn(mySocket, mSettings->mBuf, mSettings->mBounceBackBytes, 0)) == mSettings->mBounceBackBytes) {
+ struct bounceback_hdr *bbhdr = reinterpret_cast<struct bounceback_hdr *>(mSettings->mBuf);
+ now.setnow();
+ reportstruct->sentTimeRX.tv_sec = ntohl(bbhdr->bbserverRx_ts.sec);
+ reportstruct->sentTimeRX.tv_usec = ntohl(bbhdr->bbserverRx_ts.usec);
+ reportstruct->sentTimeTX.tv_sec = ntohl(bbhdr->bbserverTx_ts.sec);
+ reportstruct->sentTimeTX.tv_usec = ntohl(bbhdr->bbserverTx_ts.usec);
+ reportstruct->packetTime.tv_sec = now.getSecs();
+ reportstruct->packetTime.tv_usec = now.getUsecs();
+ reportstruct->packetLen += n;
+ reportstruct->emptyreport = 0;
+ myReportPacket();
+ } else if (n == 0) {
+ peerclose = true;
+ } else if (n < 0) {
+ if (FATALTCPREADERR(errno)) {
+ WARN_errno(1, "bounceback read");
+ peerclose = true;
+ n = 0;
+ } else {
+ WARN(1, "timeout: bounceback read");
+ }
+ }
+ } else if ((reportstruct->packetLen < 0 ) && NONFATALTCPWRITERR(errno)) {
+ reportstruct->packetLen = 0;
+ reportstruct->emptyreport = 1;
+ reportstruct->errwrite=WriteErrNoAccount;
+ myReportPacket();
+ } else {
+ reportstruct->errwrite=WriteErrFatal;
+ reportstruct->packetLen = -1;
+ FAIL_errno(1, "tcp bounce-back write", mSettings);
+ }
+ }
+ }
+ WriteTcpTxBBHdr(reportstruct, 0x0, 1);
+ disarm_itimer();
+ FinishTrafficActions();
+}
+/*
+ * UDP send loop
+ */
+double Client::get_delay_target () {
+ double delay_target;
+ if (isIPG(mSettings)) {
+ delay_target = mSettings->mBurstIPG * 1000000; // convert from milliseconds to nanoseconds
+ } else {
+ // compute delay target in units of nanoseconds
+ if (mSettings->mAppRateUnits == kRate_BW) {
+ // compute delay for bandwidth restriction, constrained to [0,1] seconds
+ delay_target = (mSettings->mBufLen * ((kSecs_to_nsecs * kBytes_to_Bits)
+ / mSettings->mAppRate));
+ } else {
+ delay_target = 1e9 / mSettings->mAppRate;
+ }
+ }
+ return delay_target;
+}
+
+void Client::RunUDP () {
+ struct UDP_datagram* mBuf_UDP = reinterpret_cast<struct UDP_datagram*>(mSettings->mBuf);
+ int currLen;
+
+ double delay_target = get_delay_target();
+ double delay = 0;
+ double adjust = 0;
+
+ // Set this to > 0 so first loop iteration will delay the IPG
+ currLen = 1;
+ double variance = mSettings->mVariance;
+ if (apply_first_udppkt_delay && (delay_target > 100000)) {
+ //the case when a UDP first packet went out in SendFirstPayload
+ delay_loop(static_cast<unsigned long>(delay_target / 1000));
+ }
+
+ while (InProgress()) {
+ // Test case: drop 17 packets and send 2 out-of-order:
+ // sequence 51, 52, 70, 53, 54, 71, 72
+ //switch(datagramID) {
+ // case 53: datagramID = 70; break;
+ // case 71: datagramID = 53; break;
+ // case 55: datagramID = 71; break;
+ // default: break;
+ //}
+ now.setnow();
+ reportstruct->writecnt = 1;
+ reportstruct->packetTime.tv_sec = now.getSecs();
+ reportstruct->packetTime.tv_usec = now.getUsecs();
+ reportstruct->sentTime = reportstruct->packetTime;
+ if (isVaryLoad(mSettings) && mSettings->mAppRateUnits == kRate_BW) {
+ static Timestamp time3;
+ if (now.subSec(time3) >= VARYLOAD_PERIOD) {
+ long var_rate = lognormal(mSettings->mAppRate,variance);
+ if (var_rate < 0)
+ var_rate = 0;
+ delay_target = (mSettings->mBufLen * ((kSecs_to_nsecs * kBytes_to_Bits) / var_rate));
+ time3 = now;
+ }
+ }
+ // store datagram ID into buffer
+ WritePacketID(reportstruct->packetID);
+ mBuf_UDP->tv_sec = htonl(reportstruct->packetTime.tv_sec);
+ mBuf_UDP->tv_usec = htonl(reportstruct->packetTime.tv_usec);
+
+ // Adjustment for the running delay
+ // o measure how long the last loop iteration took
+ // o calculate the delay adjust
+ // - If write succeeded, adjust = target IPG - the loop time
+ // - If write failed, adjust = the loop time
+ // o then adjust the overall running delay
+ // Note: adjust units are nanoseconds,
+ // packet timestamps are microseconds
+ if (currLen > 0)
+ adjust = delay_target + \
+ (1000.0 * lastPacketTime.subUsec(reportstruct->packetTime));
+ else
+ adjust = 1000.0 * lastPacketTime.subUsec(reportstruct->packetTime);
+
+ lastPacketTime.set(reportstruct->packetTime.tv_sec, reportstruct->packetTime.tv_usec);
+ // Since linux nanosleep/busyloop can exceed delay
+ // there are two possible equilibriums
+ // 1) Try to perserve inter packet gap
+ // 2) Try to perserve requested transmit rate
+ // The latter seems preferred, hence use a running delay
+ // that spans the life of the thread and constantly adjust.
+ // A negative delay means the iperf app is behind.
+ delay += adjust;
+ // Don't let delay grow unbounded
+ if (delay < delay_lower_bounds) {
+ delay = delay_target;
+ }
+
+ reportstruct->errwrite = WriteNoErr;
+ reportstruct->emptyreport = 0;
+ // perform write
+ if (isModeAmount(mSettings)) {
+ currLen = write(mySocket, mSettings->mBuf, (mSettings->mAmount < static_cast<unsigned>(mSettings->mBufLen)) ? mSettings->mAmount : mSettings->mBufLen);
+ } else {
+ currLen = write(mySocket, mSettings->mBuf, mSettings->mBufLen);
+ }
+ if (currLen < 0) {
+ reportstruct->packetID--;
+ if (FATALUDPWRITERR(errno)) {
+ reportstruct->errwrite = WriteErrFatal;
+ WARN_errno(1, "write");
+ break;
+ } else {
+ reportstruct->errwrite = WriteErrAccount;
+ currLen = 0;
+ }
+ reportstruct->emptyreport = 1;
+ }
+
+ if (isModeAmount(mSettings)) {
+ /* mAmount may be unsigned, so don't let it underflow! */
+ if (mSettings->mAmount >= static_cast<unsigned long>(currLen)) {
+ mSettings->mAmount -= static_cast<unsigned long>(currLen);
+ } else {
+ mSettings->mAmount = 0;
+ }
+ }
+
+ // report packets
+ reportstruct->packetLen = static_cast<unsigned long>(currLen);
+ reportstruct->prevPacketTime = myReport->info.ts.prevpacketTime;
+ myReportPacket();
+ reportstruct->packetID++;
+ myReport->info.ts.prevpacketTime = reportstruct->packetTime;
+ // Insert delay here only if the running delay is greater than 100 usec,
+ // otherwise don't delay and immediately continue with the next tx.
+ if (delay >= 100000) {
+ // Convert from nanoseconds to microseconds
+ // and invoke the microsecond delay
+ delay_loop(static_cast<unsigned long>(delay / 1000));
+ }
+ }
+ FinishTrafficActions();
+}
+
+/*
+ * UDP isochronous send loop
+ */
+void Client::RunUDPIsochronous () {
+ struct UDP_datagram* mBuf_UDP = reinterpret_cast<struct UDP_datagram*>(mSettings->mBuf);
+ // skip over the UDP datagram (seq no, timestamp) to reach the isoch fields
+ struct client_udp_testhdr *udp_payload = reinterpret_cast<client_udp_testhdr *>(mSettings->mBuf);
+
+ double delay_target = mSettings->mBurstIPG * 1000000; // convert from milliseconds to nanoseconds
+ double delay = 0;
+ double adjust = 0;
+ int currLen = 1;
+ int frameid=0;
+ Timestamp t1;
+
+ // make sure the packet can carry the isoch payload
+ if (!framecounter) {
+ framecounter = new Isochronous::FrameCounter(mSettings->mFPS);
+ }
+ udp_payload->isoch.burstperiod = htonl(framecounter->period_us());
+
+ int initdone = 0;
+ int fatalwrite_err = 0;
+ while (InProgress() && !fatalwrite_err) {
+ int bytecnt = static_cast<int>(lognormal(mSettings->mMean,mSettings->mVariance)) / (mSettings->mFPS * 8);
+ if (bytecnt < udp_payload_minimum)
+ bytecnt = udp_payload_minimum;
+ delay = 0;
+
+ // printf("bits=%d\n", (int) (mSettings->mFPS * bytecnt * 8));
+ udp_payload->isoch.burstsize = htonl(bytecnt);
+ udp_payload->isoch.prevframeid = htonl(frameid);
+ reportstruct->burstsize=bytecnt;
+ frameid = framecounter->wait_tick(&reportstruct->sched_err);
+ reportstruct->scheduled = true;
+ udp_payload->isoch.frameid = htonl(frameid);
+ lastPacketTime.setnow();
+ if (!initdone) {
+ initdone = 1;
+ udp_payload->isoch.start_tv_sec = htonl(lastPacketTime.getSecs());
+ udp_payload->isoch.start_tv_usec = htonl(lastPacketTime.getUsecs());
+ }
+ while ((bytecnt > 0) && InProgress()) {
+ t1.setnow();
+ reportstruct->packetTime.tv_sec = t1.getSecs();
+ reportstruct->packetTime.tv_usec = t1.getUsecs();
+ reportstruct->sentTime = reportstruct->packetTime;
+ mBuf_UDP->tv_sec = htonl(reportstruct->packetTime.tv_sec);
+ mBuf_UDP->tv_usec = htonl(reportstruct->packetTime.tv_usec);
+ WritePacketID(reportstruct->packetID);
+
+ // Adjustment for the running delay
+ // o measure how long the last loop iteration took
+ // o calculate the delay adjust
+ // - If write succeeded, adjust = target IPG - the loop time
+ // - If write failed, adjust = the loop time
+ // o then adjust the overall running delay
+ // Note: adjust units are nanoseconds,
+ // packet timestamps are microseconds
+ if (currLen > 0)
+ adjust = delay_target + \
+ (1000.0 * lastPacketTime.subUsec(reportstruct->packetTime));
+ else
+ adjust = 1000.0 * lastPacketTime.subUsec(reportstruct->packetTime);
+
+ lastPacketTime.set(reportstruct->packetTime.tv_sec, reportstruct->packetTime.tv_usec);
+ // Since linux nanosleep/busyloop can exceed delay
+ // there are two possible equilibriums
+ // 1) Try to perserve inter packet gap
+ // 2) Try to perserve requested transmit rate
+ // The latter seems preferred, hence use a running delay
+ // that spans the life of the thread and constantly adjust.
+ // A negative delay means the iperf app is behind.
+ delay += adjust;
+ // Don't let delay grow unbounded
+ // if (delay < delay_lower_bounds) {
+ // delay = delay_target;
+ // }
+
+ reportstruct->errwrite = WriteNoErr;
+ reportstruct->emptyreport = 0;
+ reportstruct->writecnt = 1;
+
+ // perform write
+ if (isModeAmount(mSettings) && (mSettings->mAmount < static_cast<unsigned>(mSettings->mBufLen))) {
+ udp_payload->isoch.remaining = htonl(mSettings->mAmount);
+ reportstruct->remaining=mSettings->mAmount;
+ currLen = write(mySocket, mSettings->mBuf, mSettings->mAmount);
+ } else {
+ udp_payload->isoch.remaining = htonl(bytecnt);
+ reportstruct->remaining=bytecnt;
+ currLen = write(mySocket, mSettings->mBuf, (bytecnt < mSettings->mBufLen) ? bytecnt : mSettings->mBufLen);
+ }
+
+ if (currLen < 0) {
+ reportstruct->packetID--;
+ reportstruct->emptyreport = 1;
+ currLen = 0;
+ if (FATALUDPWRITERR(errno)) {
+ reportstruct->errwrite = WriteErrFatal;
+ WARN_errno(1, "write");
+ fatalwrite_err = 1;
+ } else {
+ reportstruct->errwrite = WriteErrAccount;
+ }
+ } else {
+ bytecnt -= currLen;
+ if (!bytecnt)
+ reportstruct->transit_ready = 1;
+ else
+ reportstruct->transit_ready = 0;
+ // adjust bytecnt so last packet of burst is greater or equal to min packet
+ if ((bytecnt > 0) && (bytecnt < udp_payload_minimum)) {
+ bytecnt = udp_payload_minimum;
+ udp_payload->isoch.burstsize = htonl(bytecnt);
+ reportstruct->burstsize=bytecnt;
+ }
+ }
+ if (isModeAmount(mSettings)) {
+ /* mAmount may be unsigned, so don't let it underflow! */
+ if (mSettings->mAmount >= static_cast<unsigned long>(currLen)) {
+ mSettings->mAmount -= static_cast<unsigned long>(currLen);
+ } else {
+ mSettings->mAmount = 0;
+ }
+ }
+ // report packets
+
+ reportstruct->frameID=frameid;
+ reportstruct->packetLen = static_cast<unsigned long>(currLen);
+ reportstruct->prevPacketTime = myReport->info.ts.prevpacketTime;
+ myReportPacket();
+ reportstruct->scheduled = false; // reset to false after the report
+ reportstruct->packetID++;
+ myReport->info.ts.prevpacketTime = reportstruct->packetTime;
+ // Insert delay here only if the running delay is greater than 1 usec,
+ // otherwise don't delay and immediately continue with the next tx.
+ if (delay >= 1000) {
+ // Convert from nanoseconds to microseconds
+ // and invoke the microsecond delay
+ delay_loop(static_cast<unsigned long>(delay / 1000));
+ }
+ }
+ }
+ FinishTrafficActions();
+}
+// end RunUDPIsoch
+
+inline void Client::WritePacketID (intmax_t packetID) {
+ struct UDP_datagram * mBuf_UDP = reinterpret_cast<struct UDP_datagram *>(mSettings->mBuf);
+ // store datagram ID into buffer
+#ifdef HAVE_INT64_T
+ // Pack signed 64bit packetID into unsigned 32bit id1 + unsigned
+ // 32bit id2. A legacy server reading only id1 will still be able
+ // to reconstruct a valid signed packet ID number up to 2^31.
+ uint32_t id1, id2;
+ id1 = packetID & 0xFFFFFFFFLL;
+ id2 = (packetID & 0xFFFFFFFF00000000LL) >> 32;
+
+ mBuf_UDP->id = htonl(id1);
+ mBuf_UDP->id2 = htonl(id2);
+
+#ifdef HAVE_PACKET_DEBUG
+ printf("id %" PRIdMAX " (0x%" PRIxMAX ") -> 0x%x, 0x%x\n",
+ packetID, packetID, id1, id2);
+#endif
+#else
+ mBuf_UDP->id = htonl((reportstruct->packetID));
+#endif
+}
+
+inline void Client::WriteTcpTxHdr (struct ReportStruct *reportstruct, int burst_size, int burst_id) {
+ struct TCP_burst_payload * mBuf_burst = reinterpret_cast<struct TCP_burst_payload *>(mSettings->mBuf);
+ // store packet ID into buffer
+ reportstruct->packetID += burst_size;
+ mBuf_burst->start_tv_sec = htonl(myReport->info.ts.startTime.tv_sec);
+ mBuf_burst->start_tv_usec = htonl(myReport->info.ts.startTime.tv_usec);
+
+#ifdef HAVE_INT64_T
+ // Pack signed 64bit packetID into unsigned 32bit id1 + unsigned
+ // 32bit id2. A legacy server reading only id1 will still be able
+ // to reconstruct a valid signed packet ID number up to 2^31.
+ uint32_t id1, id2;
+ id1 = reportstruct->packetID & 0xFFFFFFFFLL;
+ id2 = (reportstruct->packetID & 0xFFFFFFFF00000000LL) >> 32;
+
+ mBuf_burst->seqno_lower = htonl(id1);
+ mBuf_burst->seqno_upper = htonl(id2);
+
+#ifdef HAVE_PACKET_DEBUG
+ printf("id %" PRIdMAX " (0x%" PRIxMAX ") -> 0x%x, 0x%x\n",
+ reportstruct->packetID, reportstruct->packetID, id1, id2);
+#endif
+#else
+ mBuf_burst->seqno_lower = htonl((reportstruct->packetID));
+ mBuf_burst->seqno_upper = htonl(0x0);
+#endif
+ mBuf_burst->send_tt.write_tv_sec = htonl(reportstruct->packetTime.tv_sec);
+ mBuf_burst->send_tt.write_tv_usec = htonl(reportstruct->packetTime.tv_usec);
+ mBuf_burst->burst_id = htonl((uint32_t)burst_id);
+ mBuf_burst->burst_size = htonl((uint32_t)burst_size);
+ reportstruct->frameID=burst_id;
+ reportstruct->burstsize=burst_size;
+// printf("**** Write tcp burst header size= %d id = %d\n", burst_size, burst_id);
+}
+
+// See payloads.h
+void Client::WriteTcpTxBBHdr (struct ReportStruct *reportstruct, uint32_t bbid, int final) {
+ struct bounceback_hdr * mBuf_bb = reinterpret_cast<struct bounceback_hdr *>(mSettings->mBuf);
+ // store packet ID into buffer
+ uint32_t flags = HEADER_BOUNCEBACK;
+ uint32_t bbflags = 0x0;
+ mBuf_bb->flags = htonl(flags);
+ if (isTripTime(mSettings)) {
+ bbflags |= HEADER_BBCLOCKSYNCED;
+ }
+ if (mSettings->mTOS) {
+ bbflags |= HEADER_BBTOS;
+ mBuf_bb->tos = htons((mSettings->mTOS & 0xFF));
+ }
+ if (isTcpQuickAck(mSettings)) {
+ bbflags |= HEADER_BBQUICKACK;
+ }
+ mBuf_bb->bbRunTime = 0x0;
+ if (final) {
+ bbflags |= HEADER_BBSTOP;
+ }
+ mBuf_bb->bbflags = htons(bbflags);
+ mBuf_bb->bbsize = htonl(mSettings->mBufLen);
+ mBuf_bb->bbid = htonl(bbid);
+ mBuf_bb->bbclientTx_ts.sec = htonl(reportstruct->packetTime.tv_sec);
+ mBuf_bb->bbclientTx_ts.usec = htonl(reportstruct->packetTime.tv_usec);
+ mBuf_bb->bbserverRx_ts.sec = -1;
+ mBuf_bb->bbserverRx_ts.usec = -1;
+ mBuf_bb->bbserverTx_ts.sec = -1;
+ mBuf_bb->bbserverTx_ts.usec = -1;
+ mBuf_bb->bbhold = htonl(mSettings->mBounceBackHold);
+}
+
+inline bool Client::InProgress (void) {
+ // Read the next data block from
+ // the file if it's file input
+ if (isFileInput(mSettings)) {
+ Extractor_getNextDataBlock(readAt, mSettings);
+ return Extractor_canRead(mSettings) != 0;
+ }
+ // fprintf(stderr, "DEBUG: SI=%d PC=%d T=%d A=%d\n", sInterupted, peerclose, (isModeTime(mSettings) && mEndTime.before(reportstruct->packetTime)), (isModeAmount(mSettings) && (mSettings->mAmount <= 0)));
+ return !(sInterupted || peerclose || \
+ (isModeTime(mSettings) && mEndTime.before(reportstruct->packetTime)) ||
+ (isModeAmount(mSettings) && (mSettings->mAmount <= 0)));
+}
+
+inline void Client::tcp_shutdown (void) {
+ if ((mySocket != INVALID_SOCKET) && isConnected()) {
+ int rc = shutdown(mySocket, SHUT_WR);
+#ifdef HAVE_THREAD_DEBUG
+ thread_debug("Client calls shutdown() SHUTW_WR on tcp socket %d", mySocket);
+#endif
+ WARN_errno(rc == SOCKET_ERROR, "shutdown");
+ if (!rc && !isFullDuplex(mSettings))
+ AwaitServerCloseEvent();
+ }
+}
+
+/*
+ * Common things to do to finish a traffic thread
+ *
+ * Notes on the negative packet count or seq no:
+ * A negative packet id is used to tell the server
+ * this UDP stream is terminating. The server will remove
+ * the sign. So a decrement will be seen as increments by
+ * the server (e.g, -1000, -1001, -1002 as 1000, 1001, 1002)
+ * If the retries weren't decrement here the server can get out
+ * of order packets per these retries actually being received
+ * by the server (e.g. -1000, -1000, -1000)
+ */
+void Client::FinishTrafficActions () {
+ disarm_itimer();
+ // Shutdown the TCP socket's writes as the event for the server to end its traffic loop
+ if (!isUDP(mSettings)) {
+ tcp_shutdown();
+ now.setnow();
+ reportstruct->packetTime.tv_sec = now.getSecs();
+ reportstruct->packetTime.tv_usec = now.getUsecs();
+ if (one_report) {
+ /*
+ * For TCP and if not doing interval or enhanced reporting (needed for write accounting),
+ * then report the entire transfer as one big packet
+ *
+ */
+ reportstruct->packetLen = totLen;
+ }
+ } else {
+ // stop timing
+ now.setnow();
+ reportstruct->packetTime.tv_sec = now.getSecs();
+ reportstruct->packetTime.tv_usec = now.getUsecs();
+ reportstruct->sentTime = reportstruct->packetTime;
+ // send a final terminating datagram
+ // Don't count in the mTotalLen. The server counts this one,
+ // but didn't count our first datagram, so we're even now.
+ // The negative datagram ID signifies termination to the server.
+ WritePacketID(-reportstruct->packetID);
+ struct UDP_datagram * mBuf_UDP = reinterpret_cast<struct UDP_datagram *>(mSettings->mBuf);
+ mBuf_UDP->tv_sec = htonl(reportstruct->packetTime.tv_sec);
+ mBuf_UDP->tv_usec = htonl(reportstruct->packetTime.tv_usec);
+ int len = write(mySocket, mSettings->mBuf, mSettings->mBufLen);
+#ifdef HAVE_THREAD_DEBUG
+ thread_debug("UDP client sent final packet per negative seqno %ld", -reportstruct->packetID);
+#endif
+ if (len > 0) {
+ reportstruct->packetLen = len;
+ myReportPacket();
+ }
+ reportstruct->packetLen = 0;
+ }
+ int do_close = EndJob(myJob, reportstruct);
+ if (isIsochronous(mSettings) && (myReport->info.schedule_error.cnt > 2)) {
+ fprintf(stderr,"%sIsoch schedule errors (mean/min/max/stdev) = %0.3f/%0.3f/%0.3f/%0.3f ms\n",mSettings->mTransferIDStr, \
+ ((myReport->info.schedule_error.sum / myReport->info.schedule_error.cnt) * 1e-3), (myReport->info.schedule_error.min * 1e-3), \
+ (myReport->info.schedule_error.max * 1e-3), (1e-3 * (sqrt(myReport->info.schedule_error.m2 / (myReport->info.schedule_error.cnt - 1)))));
+ }
+ if (isUDP(mSettings) && !isMulticast(mSettings) && !isNoUDPfin(mSettings)) {
+ /*
+ * For UDP, there is a final handshake between the client and the server,
+ * do that now (unless requested no to)
+ */
+ AwaitServerFinPacket();
+ }
+ if (do_close) {
+#if HAVE_THREAD_DEBUG
+ thread_debug("client close sock=%d", mySocket);
+#endif
+ int rc = close(mySocket);
+ WARN_errno(rc == SOCKET_ERROR, "client close");
+ }
+ Iperf_remove_host(mSettings);
+ FreeReport(myJob);
+ if (framecounter)
+ DELETE_PTR(framecounter);
+}
+
+/* -------------------------------------------------------------------
+ * Await for the server's fin packet which also has the server
+ * stats to displayed on the client. Attempt to re-transmit
+ * until the fin is received
+ * ------------------------------------------------------------------- */
+#define RETRYTIMER 10000 //units of us
+#define RETRYCOUNT (2 * 1000000 / RETRYTIMER) // 2 seconds worth of retries
+void Client::AwaitServerFinPacket () {
+ int rc;
+ fd_set readSet;
+ struct timeval timeout;
+ int ack_success = 0;
+ int count = RETRYCOUNT;
+ while (--count >= 0) {
+ // wait until the socket is readable, or our timeout expires
+ FD_ZERO(&readSet);
+ FD_SET(mySocket, &readSet);
+ timeout.tv_sec = 0;
+ timeout.tv_usec = RETRYTIMER;
+ rc = select(mySocket+1, &readSet, NULL, NULL, &timeout);
+ FAIL_errno(rc == SOCKET_ERROR, "select", mSettings);
+ // rc= zero means select's read timed out
+ if (rc == 0) {
+ // try to trigger another FIN by resending a negative seq no
+ WritePacketID(-(++reportstruct->packetID));
+ // write data
+ rc = write(mySocket, mSettings->mBuf, mSettings->mBufLen);
+ WARN_errno(rc < 0, "write-fin");
+#ifdef HAVE_THREAD_DEBUG
+ thread_debug("UDP client retransmit final packet per negative seqno %ld", -reportstruct->packetID);
+#endif
+ } else {
+ // socket ready to read, this packet size
+ // is set by the server. Assume it's large enough
+ // to contain the final server packet
+ rc = read(mySocket, mSettings->mBuf, MAXUDPBUF);
+
+ // dump any 2.0.13 client acks sent at the start of traffic
+ if (rc == sizeof(client_hdr_ack)) {
+ struct client_hdr_ack *ack = reinterpret_cast<struct client_hdr_ack *>(mSettings->mBuf);
+ if (ntohl(ack->typelen.type) == CLIENTHDRACK) {
+ // printf("**** dump stale ack \n");
+ continue;
+ }
+ }
+
+ WARN_errno(rc < 0, "read");
+ if (rc > 0) {
+ ack_success = 1;
+#ifdef HAVE_THREAD_DEBUG
+ thread_debug("UDP client received server relay report ack (%d)", -reportstruct->packetID);
+#endif
+ if (mSettings->mReportMode != kReport_CSV) {
+ PostReport(InitServerRelayUDPReport(mSettings, reinterpret_cast<server_hdr*>(reinterpret_cast<UDP_datagram*>(mSettings->mBuf) + 1)));
+ }
+ break;
+ }
+ }
+ }
+ if ((!ack_success) && (mSettings->mReportMode != kReport_CSV))
+ fprintf(stderr, warn_no_ack, mySocket, (isModeTime(mSettings) ? 10 : 1));
+}
+
+
+void Client::PostNullEvent (void) {
+ assert(myReport!=NULL);
+ // push a nonevent into the packet ring
+ // this will cause the reporter to process
+ // up to this event
+ memset(reportstruct, 0, sizeof(struct ReportStruct));
+ now.setnow();
+ reportstruct->packetTime.tv_sec = now.getSecs();
+ reportstruct->packetTime.tv_usec = now.getUsecs();
+ reportstruct->emptyreport=1;
+ myReportPacket();
+}
+
+void Client::PostNullEvent (bool isFirst) {
+ assert(myReport!=NULL);
+ // push a nonevent into the packet ring
+ // this will cause the reporter to process
+ // up to this event
+ memset(reportstruct, 0, sizeof(struct ReportStruct));
+ now.setnow();
+ reportstruct->packetTime.tv_sec = now.getSecs();
+ reportstruct->packetTime.tv_usec = now.getUsecs();
+ reportstruct->emptyreport=1;
+ reportstruct->scheduled = isFirst;
+ myReportPacket();
+}
+
+// The client end timer is based upon the final fin, fin-ack w/the server
+// A way to detect this is to hang a recv and wait for the zero byte
+// return indicating the socket is closed for recv per the server
+// closing it's socket
+#define MINAWAITCLOSEUSECS 2000000
+void Client::AwaitServerCloseEvent () {
+ // the await detection can take awhile so post a non event ahead of it
+ PostNullEvent();
+ unsigned int amount_usec = \
+ (isModeTime(mSettings) ? static_cast<int>(mSettings->mAmount * 10000) : MINAWAITCLOSEUSECS);
+ if (amount_usec < MINAWAITCLOSEUSECS)
+ amount_usec = MINAWAITCLOSEUSECS;
+ SetSocketOptionsReceiveTimeout(mSettings, amount_usec);
+ int rc;
+ while ((rc = recv(mySocket, mSettings->mBuf, mSettings->mBufLen, 0) > 0)) {};
+ if (rc < 0)
+ WARN_errno(1, "client await server close");
+
+ if (rc==0) {
+ connected = false;
+#ifdef HAVE_THREAD_DEBUG
+ thread_debug("Client detected server close %d", mySocket);
+#endif
+ }
+}
+
+int Client::SendFirstPayload () {
+ int pktlen = 0;
+ if (!isConnectOnly(mSettings)) {
+ if (myReport && !TimeZero(myReport->info.ts.startTime) && !(mSettings->mMode == kTest_TradeOff)) {
+ reportstruct->packetTime = myReport->info.ts.startTime;
+ } else {
+ now.setnow();
+ reportstruct->packetTime.tv_sec = now.getSecs();
+ reportstruct->packetTime.tv_usec = now.getUsecs();
+ }
+ pattern(mSettings->mBuf, mSettings->mBufLen);
+ if (isTxStartTime(mSettings)) {
+ pktlen = Settings_GenerateClientHdr(mSettings, (void *) mSettings->mBuf, mSettings->txstart_epoch);
+ } else {
+ pktlen = Settings_GenerateClientHdr(mSettings, (void *) mSettings->mBuf, reportstruct->packetTime);
+ }
+ if (pktlen > 0) {
+ if (isUDP(mSettings)) {
+ struct client_udp_testhdr *tmphdr = reinterpret_cast<struct client_udp_testhdr *>(mSettings->mBuf);
+ WritePacketID(reportstruct->packetID);
+ tmphdr->seqno_ts.tv_sec = htonl(reportstruct->packetTime.tv_sec);
+ tmphdr->seqno_ts.tv_usec = htonl(reportstruct->packetTime.tv_usec);
+ udp_payload_minimum = pktlen;
+#if HAVE_DECL_MSG_DONTWAIT
+ pktlen = send(mySocket, mSettings->mBuf, (pktlen > mSettings->mBufLen) ? pktlen : mSettings->mBufLen, MSG_DONTWAIT);
+#else
+ pktlen = send(mySocket, mSettings->mBuf, (pktlen > mSettings->mBufLen) ? pktlen : mSettings->mBufLen, 0);
+#endif
+ apply_first_udppkt_delay = true;
+ } else {
+#if HAVE_DECL_TCP_NODELAY
+ if (!isNoDelay(mSettings) && isPeerVerDetect(mSettings) && isTripTime(mSettings)) {
+ int optflag=1;
+ int rc;
+ // Disable Nagle to reduce latency of this intial message
+ if ((rc = setsockopt(mSettings->mSock, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<char *>(&optflag), sizeof(int))) < 0) {
+ WARN_errno(rc < 0, "tcpnodelay");
+ }
+ }
+#endif
+#if HAVE_DECL_MSG_DONTWAIT
+ pktlen = send(mySocket, mSettings->mBuf, pktlen, MSG_DONTWAIT);
+#else
+ pktlen = send(mySocket, mSettings->mBuf, pktlen, 0);
+#endif
+ if (isPeerVerDetect(mSettings) && !isServerReverse(mSettings)) {
+ PeerXchange();
+ }
+#if HAVE_DECL_TCP_NODELAY
+ if (!isNoDelay(mSettings) && isPeerVerDetect(mSettings) && isTripTime(mSettings)) {
+ int optflag=0;
+ int rc;
+ // Disable Nagle to reduce latency of this intial message
+ if ((rc = setsockopt(mSettings->mSock, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<char *>(&optflag), sizeof(int))) < 0) {
+ WARN_errno(rc < 0, "tcpnodelay");
+ }
+ }
+#endif
+ }
+ WARN_errno(pktlen < 0, "send_hdr");
+ }
+ }
+ return pktlen;
+}
+
+void Client::PeerXchange () {
+ int n;
+ client_hdr_ack ack;
+ /*
+ * Hang read and see if this is a header ack message
+ */
+ int readlen = isTripTime(mSettings) ? sizeof(struct client_hdr_ack) : (sizeof(struct client_hdr_ack) - sizeof(struct client_hdr_ack_ts));
+ if ((n = recvn(mySocket, reinterpret_cast<char *>(&ack), readlen, 0)) == readlen) {
+ if (ntohl(ack.typelen.type) == CLIENTHDRACK) {
+ mSettings->peer_version_u = ntohl(ack.version_u);
+ mSettings->peer_version_l = ntohl(ack.version_l);
+ if (isTripTime(mSettings)) {
+ Timestamp now;
+ Timestamp senttx(ntohl(ack.ts.sent_tv_sec), ntohl(ack.ts.sent_tv_usec));
+ Timestamp sentrx(ntohl(ack.ts.sentrx_tv_sec), ntohl(ack.ts.sentrx_tv_usec));
+ Timestamp acktx(ntohl(ack.ts.ack_tv_sec), ntohl(ack.ts.ack_tv_usec));
+ Timestamp ackrx(now.getSecs(), now.getUsecs());
+ double str = (sentrx.get() - senttx.get()) * 1e3;
+ double atr = (now.get() - acktx.get()) * 1e3;
+ double rtt = str + atr;
+ double halfrtt = rtt / 2.0;
+ fprintf(stderr,"%sClock sync check (ms): RTT/Half=(%0.3f/%0.3f) OWD-send/ack/asym=(%0.3f/%0.3f/%0.3f)\n",mSettings->mTransferIDStr, rtt, halfrtt, str, atr, (str-atr));
+ }
+ }
+ } else {
+ WARN_errno(1, "recvack");
+ }
+}
+
+/*
+ * BarrierClient allows for multiple stream clients to be syncronized
+ */
+int Client::BarrierClient (struct BarrierMutex *barrier) {
+ int last = 0;
+#ifdef HAVE_THREAD
+ assert(barrier != NULL);
+ Condition_Lock(barrier->await);
+ if (--barrier->count <= 0) {
+ // store the barrier release timer
+#ifdef HAVE_CLOCK_GETTIME
+ struct timespec t1;
+ clock_gettime(CLOCK_REALTIME, &t1);
+ barrier->release_time.tv_sec = t1.tv_sec;
+ barrier->release_time.tv_usec = t1.tv_nsec / 1000;
+#else
+ gettimeofday(&barrier->release_time, NULL);
+#endif
+ last = 1;
+ // last one wake's up everyone else
+ Condition_Broadcast(&barrier->await);
+#ifdef HAVE_THREAD_DEBUG
+ thread_debug("Barrier BROADCAST on condition %p", (void *)&barrier->await);
+#endif
+ } else {
+#ifdef HAVE_THREAD_DEBUG
+ thread_debug("Barrier WAIT on condition %p count=%d", (void *)&barrier->await, barrier->count);
+#endif
+ Condition_Wait(&barrier->await);
+ }
+ Condition_Unlock(barrier->await);
+#ifdef HAVE_THREAD_DEBUG
+ thread_debug("Barrier EXIT on condition %p", (void *)&barrier->await);
+#endif
+#else
+ last = 1;
+#endif // HAVE_THREAD
+ return last;
+}
diff --git a/src/Extractor.c b/src/Extractor.c
new file mode 100644
index 0000000..4678018
--- /dev/null
+++ b/src/Extractor.c
@@ -0,0 +1,188 @@
+/*---------------------------------------------------------------
+ * Copyright (c) 1999,2000,2001,2002,2003
+ * The Board of Trustees of the University of Illinois
+ * All Rights Reserved.
+ *---------------------------------------------------------------
+ * 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.
+ * ________________________________________________________________
+ * National Laboratory for Applied Network Research
+ * National Center for Supercomputing Applications
+ * University of Illinois at Urbana-Champaign
+ * http://www.ncsa.uiuc.edu
+ * ________________________________________________________________
+ * Extractor.cpp
+ * by Ajay Tirumala (tirumala@ncsa.uiuc.edu)
+ * -------------------------------------------------------------------
+ * Extract data from a file, used to measure the transfer rates
+ * for various stream formats.
+ *
+ * E.g. Use a gzipped file to measure the transfer rates for
+ * compressed data
+ * Use an MPEG file to measure the transfer rates of
+ * Multimedia data formats
+ * Use a plain BMP file to measure the transfer rates of
+ * Uncompressed data
+ *
+ * This is beneficial especially in measuring bandwidth across WAN
+ * links where data compression takes place before data transmission
+ * -------------------------------------------------------------------
+ */
+
+#include "Extractor.h"
+
+
+/**
+ * Constructor
+ * @arg fileName Name of the file
+ * @arg size Block size for reading
+ * Open the file and set the block size
+ */
+void Extractor_Initialize ( char *fileName, int inSize, struct thread_Settings *mSettings ) {
+
+ if ( (mSettings->Extractor_file = fopen (fileName, "rb")) == NULL ) {
+ fprintf( stderr, "Unable to open the file stream\n");
+ fprintf( stderr, "Will use the default data stream\n");
+ return;
+ }
+ mSettings->Extractor_size = inSize;
+}
+
+
+/**
+ * Constructor
+ * @arg fp File Pointer
+ * @arg size Block size for reading
+ * Set the block size,file pointer
+ */
+void Extractor_InitializeFile ( FILE *fp, int inSize, struct thread_Settings *mSettings ) {
+ mSettings->Extractor_file = fp;
+ mSettings->Extractor_size = inSize;
+}
+
+
+/**
+ * Destructor - Close the file
+ */
+void Extractor_Destroy ( struct thread_Settings *mSettings ) {
+ if ( mSettings->Extractor_file != NULL )
+ fclose( mSettings->Extractor_file );
+}
+
+
+/*
+ * Fetches the next data block from
+ * the file
+ * @arg block Pointer to the data read
+ * @return Number of bytes read
+ */
+int Extractor_getNextDataBlock ( char *data, struct thread_Settings *mSettings ) {
+ if ( Extractor_canRead( mSettings ) ) {
+ return(fread( data, 1, mSettings->Extractor_size,
+ mSettings->Extractor_file ));
+ }
+ return 0;
+}
+
+/**
+ * Function which determines whether
+ * the file stream is still readable
+ * @return boolean true, if readable; false, if not
+ */
+int Extractor_canRead ( struct thread_Settings *mSettings ) {
+ return(( mSettings->Extractor_file != NULL )
+ && !(feof( mSettings->Extractor_file )));
+}
+
+/**
+ * This is used to reduce the read size
+ * Used in UDP transfer to accomodate the
+ * the header (timestamp)
+ * @arg delta Size to reduce
+ */
+void Extractor_reduceReadSize ( int delta, struct thread_Settings *mSettings ) {
+ mSettings->Extractor_size -= delta;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Launch.cpp b/src/Launch.cpp
new file mode 100644
index 0000000..9f0c633
--- /dev/null
+++ b/src/Launch.cpp
@@ -0,0 +1,478 @@
+/*---------------------------------------------------------------
+ * Copyright (c) 1999,2000,2001,2002,2003
+ * The Board of Trustees of the University of Illinois
+ * All Rights Reserved.
+ *---------------------------------------------------------------
+ * 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.
+ * ________________________________________________________________
+ * National Laboratory for Applied Network Research
+ * National Center for Supercomputing Applications
+ * University of Illinois at Urbana-Champaign
+ * http://www.ncsa.uiuc.edu
+ * ________________________________________________________________
+ *
+ * Launch.cpp
+ * by Kevin Gibbs <kgibbs@nlanr.net>
+ * -------------------------------------------------------------------
+ * Functions to launch new server and client threads from C while
+ * the server and client are in C++.
+ * The launch function for reporters is in Reporter.c since it is
+ * in C and does not need a special launching function.
+ * ------------------------------------------------------------------- */
+
+#include "headers.h"
+#include "Thread.h"
+#include "Settings.hpp"
+#include "Client.hpp"
+#include "Listener.hpp"
+#include "Server.hpp"
+#include "PerfSocket.hpp"
+#include "active_hosts.h"
+#include "SocketAddr.h"
+#include "delay.h"
+
+static int fullduplex_startstop_barrier (struct BarrierMutex *barrier) {
+ int rc = 0;
+ assert(barrier != NULL);
+ Condition_Lock(barrier->await);
+ ++barrier->count;
+#ifdef HAVE_THREAD_DEBUG
+ thread_debug("Fullduplex barrier incr to %d %p ", barrier->count, (void *)&barrier->await, rc);
+#endif
+ if (barrier->count == 2) {
+ rc = 1;
+ // last one wake's up everyone else'
+#ifdef HAVE_THREAD_DEBUG
+ thread_debug("Fullduplex startstop broadcast on condition %p ", (void *)&barrier->await, rc);
+#endif
+ Condition_Broadcast(&barrier->await);
+ } else {
+ int timeout = barrier->timeout;
+ while ((barrier->count != 2) && (timeout > 0)) {
+#ifdef HAVE_THREAD_DEBUG
+ thread_debug("Fullduplex startstop barrier wait %p %d/2 (%d)", (void *)&barrier->await, barrier->count, timeout);
+#endif
+ Condition_TimedWait(&barrier->await, 1);
+ timeout--;
+ if ((timeout == 0) && (barrier->count != 2)) {
+ fprintf(stdout, "Barrier timeout per full duplex traffic\n");
+ Condition_Unlock(barrier->await);
+ return -1;
+ }
+ }
+ barrier->count=0;
+ }
+ Condition_Unlock(barrier->await);
+ return rc;
+}
+int fullduplex_start_barrier (struct BarrierMutex *barrier) {
+ int rc = fullduplex_startstop_barrier(barrier);
+#ifdef HAVE_THREAD_DEBUG
+ thread_debug("Fullduplex start barrier done on condition %p rc=%d", (void *)&barrier->await, rc);
+#endif
+ return rc;
+}
+int fullduplex_stop_barrier (struct BarrierMutex *barrier) {
+ int rc = fullduplex_startstop_barrier(barrier);
+#ifdef HAVE_THREAD_DEBUG
+ thread_debug("Fullduplex stop barrier done on condition %p rc=%d", (void *)&barrier->await, rc);
+#endif
+ return rc;
+}
+
+/*
+ * listener_spawn is responsible for creating a Listener class
+ * and launching the listener. It is provided as a means for
+ * the C thread subsystem to launch the listener C++ object.
+ */
+void listener_spawn(struct thread_Settings *thread) {
+ Listener *theListener = NULL;
+ // the Listener need to trigger a settings report
+ setReport(thread);
+ // start up a listener
+ theListener = new Listener(thread);
+ // Start listening
+ theListener->Run();
+ DELETE_PTR(theListener);
+}
+
+/*
+ * server_spawn is responsible for creating a Server class
+ * and launching the server. It is provided as a means for
+ * the C thread subsystem to launch the server C++ object.
+ */
+void server_spawn(struct thread_Settings *thread) {
+ Server *theServer = NULL;
+#ifdef HAVE_THREAD_DEBUG
+ if (isBounceBack(thread)) {
+ thread_debug("spawn server bounce-back");
+ } else {
+ thread_debug("spawn server settings=%p GroupSumReport=%p sock=%d", \
+ (void *) thread, (void *)thread->mSumReport, thread->mSock);
+ }
+#endif
+ // set traffic thread to realtime if needed
+#if HAVE_SCHED_SETSCHEDULER
+ thread_setscheduler(thread);
+#endif
+ // Start up the server
+ theServer = new Server(thread);
+ if (isTxStartTime(thread)) {
+ clock_usleep_abstime(&thread->txstart_epoch);
+ }
+ // Run the test
+ if (isUDP(thread)) {
+ theServer->RunUDP();
+ } else {
+ if (isBounceBack(thread)) {
+ theServer->RunBounceBackTCP();
+ } else {
+ theServer->RunTCP();
+ }
+ }
+ DELETE_PTR(theServer);
+}
+
+static void clientside_client_basic (struct thread_Settings *thread, Client *theClient) {
+ setTransferID(thread, 0);
+#ifdef HAVE_THREAD_DEBUG
+ if (isBounceBack(thread)) {
+ thread_debug("Launch: spawn client bounce-back mode, size = %d", thread->mBurstSize);
+ } else {
+ thread_debug("Launch: client spawn thread basic (sock=%d)", thread->mSock);
+ }
+#endif
+ SockAddr_remoteAddr(thread);
+ // Bounceback start delays the connect too
+ if (isBounceBack(thread)) {
+ if (isTxStartTime(thread)) {
+ theClient->mySockInit();
+ clock_usleep_abstime(&thread->txstart_epoch);
+ } else if (isTxHoldback(thread)) {
+ unsetTxHoldback(thread);
+ theClient->mySockInit();
+ clock_usleep(&thread->txholdback_timer);
+ }
+ }
+ theClient->my_connect(true);
+ if ((thread->mThreads > 1) && !isNoConnectSync(thread) && !isCompat(thread))
+ // When -P > 1 then all threads finish connect before starting traffic
+ theClient->BarrierClient(thread->connects_done);
+ if (theClient->isConnected()) {
+ if ((thread->mThreads > 1) || isSumOnly(thread))
+ Iperf_push_host(thread);
+ theClient->StartSynch();
+ theClient->Run();
+ }
+}
+
+static void clientside_client_reverse (struct thread_Settings *thread, \
+ struct thread_Settings *reverse_client, Client *theClient) {
+ setTransferID(thread, 0);
+ SockAddr_remoteAddr(thread);
+ theClient->my_connect(true);
+#ifdef HAVE_THREAD_DEBUG
+ thread_debug("Launch: client spawn thread reverse (sock=%d)", thread->mSock);
+#endif
+ if ((thread->mThreads > 1) && !isNoConnectSync(thread))
+ // When -P > 1 then all threads finish connect before starting traffic
+ theClient->BarrierClient(thread->connects_done);
+ if (theClient->isConnected()) {
+ FAIL((!reverse_client || !(thread->mSock > 0)), "Reverse test failed to start per thread settings or socket problem", thread);
+ setTransferID(reverse_client, 1);
+ theClient->StartSynch();
+ reverse_client->mSock = thread->mSock; // use the same socket for both directions
+ reverse_client->mThreadMode = kMode_Server;
+ setReverse(reverse_client);
+ setNoUDPfin(reverse_client); // disable the fin report - no need
+ reverse_client->size_local = sizeof(iperf_sockaddr);
+ getsockname(reverse_client->mSock, reinterpret_cast<sockaddr*>(&reverse_client->local), &reverse_client->size_local);
+ if ((thread->mThreads > 1) || isSumOnly(thread))
+ Iperf_push_host(reverse_client);
+ thread_start(reverse_client);
+ if (theClient->myJob)
+ FreeReport(theClient->myJob);
+ }
+}
+
+static void clientside_client_fullduplex (struct thread_Settings *thread, \
+ struct thread_Settings *reverse_client, Client *theClient) {
+ setTransferID(thread, 0);
+ SockAddr_remoteAddr(thread);
+ if (!isBounceBack(thread)) {
+ thread->mFullDuplexReport = InitSumReport(thread, -1, 1);
+ }
+ Settings_Copy(thread, &reverse_client, SHALLOW_COPY);
+ if ((thread->mThreads > 1) || isSumOnly(thread) || \
+ (!(thread->mThreads > 1) && !isEnhanced(thread))) {
+ Iperf_push_host(thread);
+ Iperf_push_host(reverse_client);
+ }
+ assert(reverse_client != NULL);
+ setTransferID(reverse_client, 1);
+ theClient->my_connect(true);
+#ifdef HAVE_THREAD_DEBUG
+ thread_debug("Launch: client spawn thread fullduplex (sock=%d)", thread->mSock);
+#endif
+ if ((thread->mThreads > 1) && !isNoConnectSync(thread))
+ // When -P > 1 then all threads finish connect before starting traffic
+ theClient->BarrierClient(thread->connects_done);
+ if (theClient->isConnected()) {
+ thread->mFullDuplexReport->info.common->socket = thread->mSock;
+ FAIL((!reverse_client || !(thread->mSock > 0)), "Reverse test failed to start per thread settings or socket problem", thread);
+ reverse_client->mSock = thread->mSock; // use the same socket for both directions
+ reverse_client->mThreadMode = kMode_Server;
+ setReverse(reverse_client);
+ if (isModeTime(reverse_client)) {
+ reverse_client->mAmount += (SLOPSECS * 100); // add 2 sec for slop on reverse, units are 10 ms
+ }
+ thread_start(reverse_client);
+ if (theClient->StartSynch() != -1) {
+ theClient->Run();
+ }
+ }
+}
+
+static void serverside_client_fullduplex (struct thread_Settings *thread, Client *theClient) {
+#ifdef HAVE_THREAD_DEBUG
+ thread_debug("Launch: Listener spawn client thread (fd sock=%d)", thread->mSock);
+#endif
+ setTransferID(thread, 1);
+ if (theClient->StartSynch() != -1) {
+ theClient->Run();
+ }
+}
+
+static void serverside_client_bidir (struct thread_Settings *thread, Client *theClient) {
+#ifdef HAVE_THREAD_DEBUG
+ thread_debug("Launch: Listener spawn client thread (bidir sock=%d)", thread->mSock);
+#endif
+ setTransferID(thread, 1);
+ SockAddr_zeroAddress(&thread->peer);
+ SockAddr_remoteAddr(thread);
+ if (thread->mReportMode == kReport_CSV) {
+ setNoSettReport(thread);
+ setNoConnReport(thread);
+ } else {
+ unsetNoSettReport(thread);
+ unsetNoConnReport(thread);
+ }
+ setReport(thread);
+ theClient->my_connect(false);
+ if (theClient->isConnected()) {
+ Iperf_push_host(thread);
+ if (theClient->StartSynch() != -1) {
+ theClient->Run();
+ }
+ }
+}
+
+/*
+ * client_spawn is responsible for creating a Client class
+ * and launching the client. It is provided as a means for
+ * the C thread subsystem to launch the client C++ object.
+ *
+ * There are a few different client startup modes
+ * o) Normal
+ * o) Dual (-d or -r) (legacy)
+ * o) Reverse (Client side) (client acts like server)
+ * o) FullDuplex (Client side) client starts server
+ * o) ServerReverse (Server side) (listener starts a client)
+ * o) FullDuplex (Server side) (listener starts server & client)
+ * o) WriteAck
+ *
+ * Note: This runs in client thread context
+ */
+void client_spawn (struct thread_Settings *thread) {
+ Client *theClient = NULL;
+
+ // set traffic thread to realtime if needed
+#if HAVE_SCHED_SETSCHEDULER
+ thread_setscheduler(thread);
+#endif
+ // start up the client
+ setTransferID(thread, 0);
+ theClient = new Client(thread);
+ // let the reporter thread go first in the case of -P greater than 1
+ Condition_Lock(reporter_state.await);
+ while (!reporter_state.ready) {
+ Condition_TimedWait(&reporter_state.await, 1);
+ }
+ Condition_Unlock(reporter_state.await);
+
+ if (isConnectOnly(thread)) {
+ theClient->ConnectPeriodic();
+ } else if (!isServerReverse(thread)) {
+ // These are the client side spawning of clients
+ if (!isReverse(thread) && !isFullDuplex(thread)) {
+ clientside_client_basic(thread, theClient);
+ } else if (isReverse(thread) && !isFullDuplex(thread)) {
+ struct thread_Settings *reverse_thread = NULL;
+ Settings_Copy(thread, &reverse_thread, DEEP_COPY);
+ FAIL((reverse_thread == NULL), "Reverse thread alloc failed", thread);
+ clientside_client_reverse(thread, reverse_thread, theClient);
+ } else if (isFullDuplex(thread)) {
+ struct thread_Settings *reverse_thread = NULL;
+ Settings_Copy(thread, &reverse_thread, DEEP_COPY);
+ FAIL((reverse_thread == NULL), "Reverse in full-duplex thread alloc failed", thread);
+ clientside_client_fullduplex(thread, reverse_thread, theClient);
+ } else {
+ fprintf(stdout, "Program error in client side client_spawn");
+ _exit(-1);
+ }
+ } else {
+ if (thread->mMode != kTest_Normal) {
+ setCompat(thread);
+ // These are the server or listener side spawning of clients
+ serverside_client_bidir(thread, theClient);
+ } else {
+ serverside_client_fullduplex(thread, theClient);
+ }
+ }
+ // Call the client's destructor
+ DELETE_PTR(theClient);
+}
+
+/*
+ * client_init handles multiple threaded connects. It creates
+ * a listener object if either the dual test or tradeoff were
+ * specified. It also creates settings structures for all the
+ * threads and arranges them so they can be managed and started
+ * via the one settings structure that was passed in.
+ *
+ * Note: This runs in main thread context
+ */
+void client_init(struct thread_Settings *clients) {
+ struct thread_Settings *itr = NULL;
+ struct thread_Settings *next = NULL;
+
+ itr = clients;
+ setReport(clients);
+ // See if we need to start a listener as well
+ Settings_GenerateListenerSettings(clients, &next);
+#ifdef HAVE_THREAD
+ if (next != NULL) {
+ // We have threads and we need to start a listener so
+ // have it ran before the client is launched
+ itr->runNow = next;
+ itr = next;
+ }
+ // For each of the needed threads create a copy of the
+ // provided settings, unsetting the report flag and add
+ // to the list of threads to start
+ for (int i = 1; i < clients->mThreads; i++) {
+ Settings_Copy(clients, &next, DEEP_COPY);
+ // printf("*****port/thread = %d/%d\n", next->mPort + i, i);
+ if (next) {
+ if (isIncrSrcIP(clients) && (clients->mLocalhost != NULL)) {
+ next->incrsrcip = i;
+ }
+ if (isIncrDstIP(clients)) {
+ next->incrdstip = i;
+ // force a setHostname
+ SockAddr_zeroAddress(&next->peer);
+ } else if (clients->mBindPort) {
+ // Increment the source port of none of the quintuple is being change or the user requests it
+ if ((!isIncrDstPort(clients) && !isIncrDstIP(clients) && !isIncrSrcIP(clients)) || isIncrSrcPort(clients)) {
+ // case -B with src port and -P > 1
+ next->incrsrcport = i;
+ }
+ }
+ if (isIncrDstPort(clients)) {
+ next->mPort += i;
+ SockAddr_zeroAddress(&next->peer);
+ }
+ }
+ itr->runNow = next;
+ itr = next;
+ }
+ if ((isBounceBack(clients) || isConnectOnly(clients) || isPeriodicBurst(clients)) \
+ && (isWorkingLoadUp(clients) || isWorkingLoadDown(clients))) {
+ int working_load_threads = (clients->mWorkingLoadThreads == 0) ? 1 : clients->mWorkingLoadThreads;
+ while (working_load_threads--) {
+ Settings_Copy(clients, &next, DEEP_COPY);
+ if (next != NULL) {
+ unsetBounceBack(next);
+ unsetConnectOnly(next);
+ unsetPeriodicBurst(next);
+ unsetTxHoldback(next); // don't delay working load threads
+ next->mTOS = 0; // disable any QoS on the congestion stream
+#if HAVE_DECL_TCP_NOTSENT_LOWAT
+ next->mWritePrefetch = SMALL_WRITE_PREFETCH;
+ setWritePrefetch(next);
+#endif
+ if (isWorkingLoadUp(clients) && isWorkingLoadDown(clients)) {
+ setFullDuplex(next);
+ } else if (isWorkingLoadDown(clients)) {
+ setReverse(next);
+ }
+ if (isBounceBack(clients) && (clients->mWorkingLoadThreads > 1)) {
+ Iperf_push_host(clients);
+ }
+ // Bump the bounceback time to include the delay time
+ if (next->txholdback_timer.tv_sec || next->txholdback_timer.tv_usec) {
+ // mAmount units are 10 ms
+ next->mAmount += (next->txholdback_timer.tv_sec * 100);
+ next->mAmount += (next->txholdback_timer.tv_usec / 10000);
+ }
+ itr->runNow = next;
+ itr = next;
+ }
+ }
+ }
+#else
+ if (next != NULL) {
+ // We don't have threads and we need to start a listener so
+ // have it ran after the client is finished
+ itr->runNext = next;
+ }
+#endif
+}
+
+void listeners_init(struct thread_Settings *listener) {
+ struct thread_Settings *itr = listener;
+ struct thread_Settings *next = NULL;
+ for (int ix = 1; ix < (listener->mPortLast - listener->mPort + 1); ix++) {
+ Settings_Copy(listener, &next, DEEP_COPY);
+ if (next != NULL) {
+ setNoSettReport(next);
+ next->mPort = listener->mPort + ix;
+ next->mThreadMode = kMode_Listener;
+ itr->runNow = next;
+ itr = next;
+ }
+ }
+}
diff --git a/src/Listener.cpp b/src/Listener.cpp
new file mode 100644
index 0000000..a517cbc
--- /dev/null
+++ b/src/Listener.cpp
@@ -0,0 +1,1447 @@
+/*---------------------------------------------------------------
+ * Copyright (c) 1999,2000,2001,2002,2003
+ * The Board of Trustees of the University of Illinois
+ * All Rights Reserved.
+ *---------------------------------------------------------------
+ * 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 CONTRIBUTORS 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.
+ * ________________________________________________________________
+ * National Laboratory for Applied Network Research
+ * National Center for Supercomputing Applications
+ * University of Illinois at Urbana-Champaign
+ * http://www.ncsa.uiuc.edu
+ * ________________________________________________________________
+ *
+ * Listener.cpp
+ * by Mark Gates <mgates@nlanr.net>
+ * & Ajay Tirumala <tirumala@ncsa.uiuc.edu>
+ * rewritten by Robert McMahon
+ * -------------------------------------------------------------------
+ * Listener sets up a socket listening on the server host. For each
+ * connected socket that accept() returns, this creates a Server
+ * socket and spawns a thread for it.
+ *
+ * Changes to the latest version. Listener will run as a daemon
+ * Multicast Server is now Multi-threaded
+ * -------------------------------------------------------------------
+ * headers
+ * uses
+ * <stdlib.h>
+ * <stdio.h>
+ * <string.h>
+ * <errno.h>
+ *
+ * <sys/types.h>
+ * <unistd.h>
+ *
+ * <netdb.h>
+ * <netinet/in.h>
+ * <sys/socket.h>
+ * ------------------------------------------------------------------- */
+#define HEADERS()
+
+#include "headers.h"
+#include "Listener.hpp"
+#include "SocketAddr.h"
+#include "PerfSocket.hpp"
+#include "active_hosts.h"
+#include "util.h"
+#include "version.h"
+#include "Locale.h"
+#include "SocketAddr.h"
+#include "payloads.h"
+#include "delay.h"
+
+/* -------------------------------------------------------------------
+
+ * Stores local hostname and socket info.
+ * ------------------------------------------------------------------- */
+
+Listener::Listener (thread_Settings *inSettings) {
+ mClients = inSettings->mThreads;
+ ListenSocket = INVALID_SOCKET;
+ /*
+ * These thread settings are stored in three places
+ *
+ * 1) Listener thread
+ * 2) Reporter Thread (per the ReportSettings())
+ * 3) Server thread
+ */
+ mSettings = inSettings;
+} // end Listener
+
+/* -------------------------------------------------------------------
+ * Delete memory (buffer).
+ * ------------------------------------------------------------------- */
+Listener::~Listener () {
+#if HAVE_THREAD_DEBUG
+ thread_debug("Listener destructor close sock=%d", ListenSocket);
+#endif
+ if (ListenSocket != INVALID_SOCKET) {
+ int rc = close(ListenSocket);
+ WARN_errno(rc == SOCKET_ERROR, "listener close");
+ }
+} // end ~Listener
+
+/* -------------------------------------------------------------------
+ * This is the main Listener thread loop, listens and accept new
+ * connections and starts traffic threads
+ *
+ * Flow is
+ * o) suspend on traffic done for single client case
+ * o) hang a select() then accept() on the listener socket
+ * o) read or, more accurately, peak the socket for initial messages
+ * o) determine and set server's settings flags
+ * o) instantiate new settings for listener's clients if needed
+ * o) instantiate and bind sum and bidir report objects as needed
+ * o) start the threads needed
+ *
+ * ------------------------------------------------------------------- */
+void Listener::Run () {
+ // mCount is set True if -P was passed to the server
+ int mCount = ((mSettings->mThreads != 0) ? mSettings->mThreads : -1);
+
+ // This is a listener launched by the client per -r or -d
+ if (mSettings->clientListener) {
+ SockAddr_remoteAddr(mSettings);
+ }
+ if (!isUDP(mSettings)) {
+ // TCP needs just one listen
+ my_listen(); // This will set ListenSocket to a new sock fd
+ }
+ bool mMode_Time = isServerModeTime(mSettings) && !isDaemon(mSettings);
+ if (mMode_Time) {
+ mEndTime.setnow();
+ mEndTime.add(mSettings->mAmount / 100.0);
+ } else if (isPermitKey(mSettings) && (mSettings->mListenerTimeout > 0)) {
+ mEndTime.setnow();
+ mEndTime.add(mSettings->mListenerTimeout);
+ }
+ Timestamp now;
+#define SINGLECLIENTDELAY_DURATION 50000 // units is microseconds
+ while (!sInterupted && mCount) {
+#ifdef HAVE_THREAD_DEBUG
+ thread_debug("Listener main loop port %d ", mSettings->mPort);
+#endif
+ now.setnow();
+ if ((mMode_Time || (mSettings->mListenerTimeout > 0)) && mEndTime.before(now)) {
+#ifdef HAVE_THREAD_DEBUG
+ thread_debug("Listener port %d (loop timer expired)", mSettings->mPort);
+#endif
+ break;
+ }
+ // Serialize in the event the -1 option or --singleclient is set
+ int tc;
+ if ((isSingleClient(mSettings) || isMulticast(mSettings)) && \
+ mCount && (tc = (thread_numtrafficthreads()) > 0)) {
+ // Start with a delay in the event some traffic
+ // threads are pending to be scheduled and haven't
+ // had a chance to update the traffic thread count.
+ // An event system between listener thread and traffic threads
+ // might better but also more complex. This delay
+ // really should be good enough unless the os scheduler sucks
+ delay_loop(SINGLECLIENTDELAY_DURATION);
+#ifdef HAVE_THREAD_DEBUG
+ thread_debug("Listener single client loop mc/t/mcast/sc %d/%d/%d/%d",mCount, tc, isMulticast(mSettings), isSingleClient(mSettings));
+#endif
+ continue;
+ }
+ if (isUDP(mSettings)) {
+ // UDP needs a new listen per every new socket
+ my_listen(); // This will set ListenSocket to a new sock fd
+ }
+ // Use a select() with a timeout if -t is set or if this is a v1 -r or -d test
+ fd_set set;
+ if (((mMode_Time || isCompat(mSettings)) && (!isPermitKey(mSettings) && (mSettings->mAmount != 0))) \
+ || (isPermitKey(mSettings) && (mSettings->mListenerTimeout != 0))) {
+ // Hang a select w/timeout on the listener socket
+ struct timeval timeout;
+ if (!isPermitKey(mSettings)) {
+ timeout.tv_sec = mSettings->mAmount / 100;
+ timeout.tv_usec = (mSettings->mAmount % 100) * 10000;
+ } else {
+ timeout.tv_sec = static_cast<long>(mSettings->mListenerTimeout);
+ timeout.tv_usec = (static_cast<long>(mSettings->mListenerTimeout) * 1000000) % 1000000;
+ }
+ if (isTxStartTime(mSettings)) {
+ now.setnow();
+ long adjsecs = (mSettings->txstart_epoch.tv_sec - now.getSecs());
+ if (adjsecs > 0)
+ timeout.tv_sec += adjsecs + 1;
+ }
+ FD_ZERO(&set);
+ FD_SET(ListenSocket, &set);
+ if (!(select(ListenSocket + 1, &set, NULL, NULL, &timeout) > 0)) {
+#ifdef HAVE_THREAD_DEBUG
+ thread_debug("Listener select timeout");
+#endif
+ if (isCompat(mSettings)) {
+ fprintf(stderr, "ERROR: expected reverse connect did not occur\n");
+ break;
+ } else
+ continue;
+ }
+ }
+ if (!setsock_blocking(mSettings->mSock, true)) {
+ WARN(1, "Failed setting socket to blocking mode");
+ }
+ // Instantiate another settings object to be used by the server thread
+ Settings_Copy(mSettings, &server, DEEP_COPY);
+ FAIL(!server, "Failed memory allocation for server settings", mSettings);
+ server->mThreadMode = kMode_Server;
+ if (!isDataReport(mSettings))
+ setNoDataReport(server);
+
+ // accept a new socket and assign it to the server thread
+ int accept_sock = my_accept(server);
+ if (!(accept_sock > 0)) {
+ assert(server != mSettings);
+#ifdef HAVE_THREAD_DEBUG
+ thread_debug("Listener thread accept fail %d", accept_sock);
+#endif
+ Settings_Destroy(server);
+ continue;
+ }
+
+#ifdef HAVE_THREAD_DEBUG
+ thread_debug("Listener thread accepted server sock=%d transferID", server->mSock, server->mTransferID);
+#endif
+ // Decrement the -P counter, commonly usd to kill the listener
+ // after one test, i.e. -s -P 1
+ if (mCount > 0) {
+ mCount--;
+ }
+ // These are some exception cases where the accepted socket shouldn't have been
+ // accepted but the accept() was first required to figure this out
+ //
+ // 1) When a client started the listener per -d or -r (but not --reverse.)
+ // What's done here is to see if the server peer opening the
+ // socket matches the expected peer per a compare of the ip addresses
+ // For the case of a *client Listener* the server and host must match
+ // Note: it's a good idea to prefer --reverse and full duplex socket vs this
+ // -d,-r legacy approach. Still support it though in the name of legacy usage
+ //
+ // 2) The peer is using a V6 address but the listener/server didn't get -V (for v6) on
+ // it's command line
+ //
+ if ((mSettings->clientListener && SockAddr_Hostare_Equal(&mSettings->peer, &server->peer)) || \
+ (!isIPV6(mSettings) && SockAddr_isIPv6(&server->peer))) {
+ // Not allowed, reset things and restart the loop
+ // Don't forget to delete the UDP entry (inserted in my_accept)
+ Iperf_remove_host(server);
+ if (!isUDP(server))
+ close(server->mSock);
+ assert(server != mSettings);
+ Settings_Destroy(server);
+ continue;
+ }
+ // isCompat is a version 1.7 test, basically it indicates there is nothing
+ // in the first messages so don't try to process them. Later iperf versions use
+ // the first message to convey test request and test settings information. This flag
+ // is also used for threads that are children so-to-speak, e.g. a -d or -r client,
+ // which cannot have test flags otherwise there would be "test setup recursion"
+ // Time to read the very first packet received (per UDP) or the test flags (TCP)
+ // to get the client's requested test information.
+ // Note 1: It's important to know that this will also populate mBuf with
+ // enough information for the listener to perform test info exchange later in the code
+ // Note 2: The mBuf read is a peek so the server's traffic thread started later
+ // will also process the first message from an accounting perspective.
+ // This is required for accurate traffic statistics
+ if (!isCompat(server) && (isConnectOnly(server) || !apply_client_settings(server))) {
+ if (isConnectionReport(server) && !isSumOnly(server)) {
+ struct ReportHeader *reporthdr = InitConnectionReport(server);
+ struct ConnectionInfo *cr = static_cast<struct ConnectionInfo *>(reporthdr->this_report);
+ cr->connect_timestamp.tv_sec = server->accept_time.tv_sec;
+ cr->connect_timestamp.tv_usec = server->accept_time.tv_usec;
+ assert(reporthdr);
+ PostReport(reporthdr);
+ }
+ Iperf_remove_host(server);
+ close(server->mSock);
+ assert(server != mSettings);
+ Settings_Destroy(server);
+ continue;
+ }
+ // server settings flags should now be set per the client's first message exchange
+ // so the server setting's flags per the client can now be checked
+ if (isUDP(server)){
+ if (!isCompat(mSettings) && !isTapDev(mSettings) && (isL2LengthCheck(mSettings) || isL2LengthCheck(server)) && !L2_setup(server, server->mSock)) {
+ // Requested L2 testing but L2 setup failed
+ Iperf_remove_host(server);
+ assert(server != mSettings);
+ Settings_Destroy(server);
+ continue;
+ }
+ }
+ // Force compat mode to use 64 bit seq numbers
+ if (isUDP(server) && isCompat(mSettings)) {
+ setSeqNo64b(server);
+ }
+ setTransferID(server, 0);
+ if ((mSettings->mReportMode == kReport_CSV) && server->mSumReport && !server->mSumReport->sum_reverse_set) {
+ format_ips_port_string(&server->mSumReport->info, 1);
+ server->mSumReport->sum_reverse_set = true;
+ } else if (isTripTime(server) && server->mSumReport) {
+ SetSumHandlers(server, server->mSumReport); // this needs to be done again after first read
+ }
+
+ // Read any more test settings and test values (not just the flags) and instantiate
+ // any settings objects for client threads (e.g. bidir or full duplex)
+ // This will set the listener_client_settings to NULL if
+ // there is no need for the Listener to start a client
+ //
+ // Note: the packet payload pointer for this information has different
+ // offsets per TCP or UDP. Basically, TCP starts at byte 0 but UDP
+ // has to skip over the UDP seq no, etc.
+ //
+ if (!isCompat(server) && !isCompat(mSettings) && \
+ (isFullDuplex(server) || isServerReverse(server) || (server->mMode != kTest_Normal))) {
+ thread_Settings *listener_client_settings = NULL;
+ // read client header for reverse settings
+ Settings_GenerateClientSettings(server, &listener_client_settings, server->mBuf);
+ if (listener_client_settings) {
+ if (server->mMode != kTest_Normal)
+ listener_client_settings->mTransferID = 0;
+ setTransferID(listener_client_settings, 1);
+ if (isFullDuplex(listener_client_settings) || isReverse(listener_client_settings))
+ Iperf_push_host(listener_client_settings);
+ if (isFullDuplex(server)) {
+ assert(server->mSumReport != NULL);
+ if (!server->mSumReport->sum_fd_set) {
+ // Reset the sum output routine for the server sum report
+ // now that it's know to be full duplex. This wasn't known
+ // during accept()
+ SetSumHandlers(server, server->mSumReport);
+ server->mSumReport->sum_fd_set = 1;
+ }
+ server->mFullDuplexReport = InitSumReport(server, server->mSock, 1);
+ listener_client_settings->mFullDuplexReport = server->mFullDuplexReport;
+#if HAVE_THREAD_DEBUG
+ thread_debug("FullDuplex report client=%p/%p server=%p/%p", (void *) listener_client_settings, (void *) listener_client_settings->mFullDuplexReport, (void *) server, (void *) server->mFullDuplexReport);
+#endif
+ server->runNow = listener_client_settings;
+ } else if (server->mMode != kTest_Normal) {
+#if HAVE_THREAD_DEBUG
+ thread_debug("V1 test (-d or -r) sum report client=%p/%p server=%p/%p", (void *) listener_client_settings, (void *) listener_client_settings->mFullDuplexReport, (void *) server, (void *) server->mFullDuplexReport);
+#endif
+ if (listener_client_settings->mMode == kTest_DualTest) {
+#ifdef HAVE_THREAD
+ server->runNow = listener_client_settings;
+#else
+ server->runNext = listener_client_settings;
+#endif
+ } else {
+ server->runNext = listener_client_settings;
+ }
+ }
+ }
+ }
+ setTransferID(server, 0);
+ if (isConnectionReport(server) && !isSumOnly(server)) {
+ struct ReportHeader *reporthdr = InitConnectionReport(server);
+ struct ConnectionInfo *cr = static_cast<struct ConnectionInfo *>(reporthdr->this_report);
+ cr->connect_timestamp.tv_sec = server->accept_time.tv_sec;
+ cr->connect_timestamp.tv_usec = server->accept_time.tv_usec;
+ assert(reporthdr);
+ PostReport(reporthdr);
+ }
+ // Now start the server side traffic threads
+ thread_start_all(server);
+ }
+#ifdef HAVE_THREAD_DEBUG
+ thread_debug("Listener exiting port/sig/threads %d/%d/%d", mSettings->mPort, sInterupted, mCount);
+#endif
+} // end Run
+
+/* -------------------------------------------------------------------
+ * Setup a socket listening on a port.
+ * For TCP, this calls bind() and listen().
+ * For UDP, this just calls bind().
+ * If inLocalhost is not null, bind to that address rather than the
+ * wildcard server address, specifying what incoming interface to
+ * accept connections on.
+ * ------------------------------------------------------------------- */
+void Listener::my_listen () {
+ int rc;
+ int type;
+ int domain;
+ SockAddr_localAddr(mSettings);
+
+#if (((HAVE_TUNTAP_TUN) || (HAVE_TUNTAP_TAP)) && (AF_PACKET))
+ if (isTapDev(mSettings)) {
+ ListenSocket = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
+ FAIL_errno(ListenSocket == SOCKET_ERROR, "tuntap socket()", mSettings);
+ mSettings->mSock = ListenSocket;
+ rc = SockAddr_v4_Accept_BPF(ListenSocket, mSettings->mPort);
+ WARN_errno((rc == SOCKET_ERROR), "tap accept bpf");
+ SetSocketOptions(mSettings);
+ } else
+#endif
+ {
+ // create an AF_INET socket for the accepts
+ // for the case of L2 testing and UDP, a new AF_PACKET
+ // will be created to supercede this one
+ type = (isUDP(mSettings) ? SOCK_DGRAM : SOCK_STREAM);
+ domain = (SockAddr_isIPv6(&mSettings->local) ?
+#if HAVE_IPV6
+ AF_INET6
+#else
+ AF_INET
+#endif
+ : AF_INET);
+
+#ifdef WIN32
+ if (SockAddr_isMulticast(&mSettings->local)) {
+ // Multicast on Win32 requires special handling
+ ListenSocket = WSASocket(domain, type, 0, 0, 0, WSA_FLAG_MULTIPOINT_C_LEAF | WSA_FLAG_MULTIPOINT_D_LEAF);
+ WARN_errno(ListenSocket == INVALID_SOCKET, "socket");
+
+ } else
+#endif
+ {
+ ListenSocket = socket(domain, type, 0);
+ WARN_errno(ListenSocket == INVALID_SOCKET, "socket");
+ }
+ mSettings->mSock = ListenSocket;
+ SetSocketOptions(mSettings);
+ // reuse the address, so we can run if a former server was killed off
+ int boolean = 1;
+ Socklen_t len = sizeof(boolean);
+ rc = setsockopt(ListenSocket, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<char*>(&boolean), len);
+ // bind socket to server address
+#ifdef WIN32
+ if (SockAddr_isMulticast(&mSettings->local)) {
+ // Multicast on Win32 requires special handling
+ rc = WSAJoinLeaf(ListenSocket, (sockaddr*) &mSettings->local, mSettings->size_local,0,0,0,0,JL_BOTH);
+ WARN_errno(rc == SOCKET_ERROR, "WSAJoinLeaf (aka bind)");
+ } else
+#endif
+ {
+ rc = bind(ListenSocket, reinterpret_cast<sockaddr*>(&mSettings->local), mSettings->size_local);
+ FAIL_errno(rc == SOCKET_ERROR, "listener bind", mSettings);
+ }
+ }
+
+ // update the reporter thread
+ if (isReport(mSettings) && isSettingsReport(mSettings)) {
+ struct ReportHeader *report_settings = InitSettingsReport(mSettings);
+ assert(report_settings != NULL);
+ // disable future settings reports, listener should only do it once
+ unsetReport(mSettings);
+ PostReport(report_settings);
+ }
+
+ // listen for connections (TCP only).
+ // use large (INT_MAX) backlog allowing multiple simultaneous connections
+ if (!isUDP(mSettings)) {
+ if (isSingleClient(mSettings)) {
+ rc = listen(ListenSocket, 0 + mSettings->mThreads);
+ } else {
+ rc = listen(ListenSocket, INT_MAX);
+ }
+ WARN_errno(rc == SOCKET_ERROR, "listen");
+ } else {
+#ifndef WIN32
+ // if UDP and multicast, join the group
+ if (SockAddr_isMulticast(&mSettings->local)) {
+#ifdef HAVE_MULTICAST
+ my_multicast_join();
+#else
+ fprintf(stderr, "Multicast not supported");
+#endif // HAVE_MULTICAST
+ }
+#endif
+ }
+} // end my_listen()
+
+/* -------------------------------------------------------------------
+ * Joins the multicast group or source and group (SSM S,G)
+ *
+ * taken from: https://www.ibm.com/support/knowledgecenter/en/SSLTBW_2.1.0/com.ibm.zos.v2r1.hale001/ipv6d0141001708.htm
+ *
+ * Multicast function IPv4 IPv6 Protocol-independent
+ * ================== ==== ==== ====================
+ * Level of specified option on setsockopt()/getsockopt() IPPROTO_IP IPPROTO_IPV6 IPPROTO_IP or IPPROTO_IPV6
+ * Join a multicast group IP_ADD_MEMBERSHIP IPV6_JOIN_GROUP MCAST_JOIN_GROUP
+ * Leave a multicast group or leave all sources of that
+ * multicast group IP_DROP_MEMBERSHIP IPV6_LEAVE_GROUP MCAST_LEAVE_GROUP
+ * Select outbound interface for sending multicast datagrams IP_MULTICAST_IF IPV6_MULTICAST_IF NA
+ * Set maximum hop count IP_MULTICAST_TTL IPV6_MULTICAST_HOPS NA
+ * Enable multicast loopback IP_MULTICAST_LOOP IPV6_MULTICAST_LOOP NA
+ * Join a source multicast group IP_ADD_SOURCE_MEMBERSHIP NA MCAST_JOIN_SOURCE_GROUP
+ * Leave a source multicast group IP_DROP_SOURCE_MEMBERSHIP NA MCAST_LEAVE_SOURCE_GROUP
+ * Block data from a source to a multicast group IP_BLOCK_SOURCE NA MCAST_BLOCK_SOURCE
+ * Unblock a previously blocked source for a multicast group IP_UNBLOCK_SOURCE NA MCAST_UNBLOCK_SOURCE
+ *
+ *
+ * Reminder: The os will decide which version of IGMP or MLD to use. This may be controlled by system settings, e.g.:
+ *
+ * [rmcmahon@lvnvdb0987:~/Code/ssm/iperf2-code] $ sysctl -a | grep mld | grep force
+ * net.ipv6.conf.all.force_mld_version = 0
+ * net.ipv6.conf.default.force_mld_version = 0
+ * net.ipv6.conf.lo.force_mld_version = 0
+ * net.ipv6.conf.eth0.force_mld_version = 0
+ *
+ * [rmcmahon@lvnvdb0987:~/Code/ssm/iperf2-code] $ sysctl -a | grep igmp | grep force
+ * net.ipv4.conf.all.force_igmp_version = 0
+ * net.ipv4.conf.default.force_igmp_version = 0
+ * net.ipv4.conf.lo.force_igmp_version = 0
+ * net.ipv4.conf.eth0.force_igmp_version = 0
+ *
+ * ------------------------------------------------------------------- */
+void Listener::my_multicast_join () {
+ // This is the older mulitcast join code. Both SSM and binding the
+ // an interface requires the newer socket options. Using the older
+ // code here will maintain compatiblity with previous iperf versions
+ if (!isSSMMulticast(mSettings) && !mSettings->mIfrname) {
+ if (!SockAddr_isIPv6(&mSettings->local)) {
+ struct ip_mreq mreq;
+ memcpy(&mreq.imr_multiaddr, SockAddr_get_in_addr(&mSettings->local), \
+ sizeof(mreq.imr_multiaddr));
+ mreq.imr_interface.s_addr = htonl(INADDR_ANY);
+ int rc = setsockopt(ListenSocket, IPPROTO_IP, IP_ADD_MEMBERSHIP,
+ reinterpret_cast<char*>(&mreq), sizeof(mreq));
+ WARN_errno(rc == SOCKET_ERROR, "multicast join");
+#if HAVE_DECL_IP_MULTICAST_ALL
+ int mc_all = 0;
+ rc = setsockopt(ListenSocket, IPPROTO_IP, IP_MULTICAST_ALL, (void*) &mc_all, sizeof(mc_all));
+ WARN_errno(rc == SOCKET_ERROR, "ip_multicast_all");
+#endif
+ } else {
+#if (HAVE_IPV6 && HAVE_IPV6_MULTICAST && (HAVE_DECL_IPV6_JOIN_GROUP || HAVE_DECL_IPV6_ADD_MEMBERSHIP))
+ struct ipv6_mreq mreq;
+ memcpy(&mreq.ipv6mr_multiaddr, SockAddr_get_in6_addr(&mSettings->local), sizeof(mreq.ipv6mr_multiaddr));
+ mreq.ipv6mr_interface = 0;
+#if HAVE_DECL_IPV6_JOIN_GROUP
+ int rc = setsockopt(ListenSocket, IPPROTO_IPV6, IPV6_JOIN_GROUP, \
+ reinterpret_cast<char*>(&mreq), sizeof(mreq));
+#else
+ int rc = setsockopt(ListenSocket, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, \
+ reinterpret_cast<char*>(&mreq), sizeof(mreq));
+#endif
+ FAIL_errno(rc == SOCKET_ERROR, "multicast v6 join", mSettings);
+#else
+ fprintf(stderr, "IPv6 multicast is not supported on this platform\n");
+#endif
+ }
+ } else {
+ int rc;
+#ifdef HAVE_SSM_MULTICAST
+ // Here it's either an SSM S,G multicast join or a *,G with an interface specifier
+ // Use the newer socket options when these are specified
+ socklen_t socklen = sizeof(struct sockaddr_storage);
+ int iface=0;
+#ifdef HAVE_NET_IF_H
+ /* Set the interface or any */
+ if (mSettings->mIfrname) {
+ iface = if_nametoindex(mSettings->mIfrname);
+ FAIL_errno(!iface, "mcast if_nametoindex",mSettings);
+ } else {
+ iface = 0;
+ }
+#endif
+
+ if (isIPV6(mSettings)) {
+#if HAVE_IPV6_MULTICAST
+ if (mSettings->mSSMMulticastStr) {
+ struct group_source_req group_source_req;
+ struct sockaddr_in6 *group;
+ struct sockaddr_in6 *source;
+
+ memset(&group_source_req, 0, sizeof(struct group_source_req));
+
+ group_source_req.gsr_interface = iface;
+ group=reinterpret_cast<struct sockaddr_in6*>(&group_source_req.gsr_group);
+ source=reinterpret_cast<struct sockaddr_in6*>(&group_source_req.gsr_source);
+ source->sin6_family = AF_INET6;
+ group->sin6_family = AF_INET6;
+ /* Set the group */
+ rc=getsockname(ListenSocket,reinterpret_cast<struct sockaddr *>(group), &socklen);
+ FAIL_errno(rc == SOCKET_ERROR, "mcast join source group getsockname",mSettings);
+ group->sin6_port = 0; /* Ignored */
+
+ /* Set the source, apply the S,G */
+ rc=inet_pton(AF_INET6, mSettings->mSSMMulticastStr,&source->sin6_addr);
+ FAIL_errno(rc != 1, "mcast v6 join source group pton",mSettings);
+ source->sin6_port = 0; /* Ignored */
+#ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_LEN
+ source->sin6_len = group->sin6_len;
+#endif
+ rc = -1;
+#if HAVE_DECL_MCAST_JOIN_SOURCE_GROUP
+ rc = setsockopt(ListenSocket,IPPROTO_IPV6,MCAST_JOIN_SOURCE_GROUP, reinterpret_cast<const char *>(&group_source_req),
+ sizeof(group_source_req));
+#endif
+ FAIL_errno(rc == SOCKET_ERROR, "mcast v6 join source group",mSettings);
+ } else {
+ struct group_req group_req;
+ struct sockaddr_in6 *group;
+
+ memset(&group_req, 0, sizeof(struct group_req));
+
+ group_req.gr_interface = iface;
+ group=reinterpret_cast<struct sockaddr_in6*>(&group_req.gr_group);
+ group->sin6_family = AF_INET6;
+ /* Set the group */
+ rc=getsockname(ListenSocket,reinterpret_cast<struct sockaddr *>(group), &socklen);
+ FAIL_errno(rc == SOCKET_ERROR, "mcast v6 join group getsockname",mSettings);
+ group->sin6_port = 0; /* Ignored */
+ rc = -1;
+#if HAVE_DECL_MCAST_JOIN_GROUP
+ rc = setsockopt(ListenSocket,IPPROTO_IPV6,MCAST_JOIN_GROUP, reinterpret_cast<const char *>(&group_req),
+ sizeof(group_source_req));
+#endif
+ FAIL_errno(rc == SOCKET_ERROR, "mcast v6 join group",mSettings);
+ }
+#else
+ fprintf(stderr, "Unfortunately, IPv6 multicast is not supported on this platform\n");
+#endif
+ } else {
+ if (mSettings->mSSMMulticastStr) {
+ struct sockaddr_in *group;
+ struct sockaddr_in *source;
+
+ // Fill out both structures because we don't which one will succeed
+ // and both may need to be tried
+#ifdef HAVE_STRUCT_IP_MREQ_SOURCE
+ struct ip_mreq_source imr;
+ memset (&imr, 0, sizeof (imr));
+#endif
+#ifdef HAVE_STRUCT_GROUP_SOURCE_REQ
+ struct group_source_req group_source_req;
+ memset(&group_source_req, 0, sizeof(struct group_source_req));
+ group_source_req.gsr_interface = iface;
+ group=reinterpret_cast<struct sockaddr_in*>(&group_source_req.gsr_group);
+ source=reinterpret_cast<struct sockaddr_in*>(&group_source_req.gsr_source);
+#else
+ struct sockaddr_in imrgroup;
+ struct sockaddr_in imrsource;
+ group = &imrgroup;
+ source = &imrsource;
+#endif
+ source->sin_family = AF_INET;
+ group->sin_family = AF_INET;
+ /* Set the group */
+ rc=getsockname(ListenSocket,reinterpret_cast<struct sockaddr *>(group), &socklen);
+ FAIL_errno(rc == SOCKET_ERROR, "mcast join source group getsockname",mSettings);
+ group->sin_port = 0; /* Ignored */
+
+ /* Set the source, apply the S,G */
+ rc=inet_pton(AF_INET,mSettings->mSSMMulticastStr,&source->sin_addr);
+ FAIL_errno(rc != 1, "mcast join source pton",mSettings);
+#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
+ source->sin_len = group->sin_len;
+#endif
+ source->sin_port = 0; /* Ignored */
+ rc = -1;
+
+#if HAVE_DECL_MCAST_JOIN_SOURCE_GROUP
+ rc = setsockopt(ListenSocket,IPPROTO_IP,MCAST_JOIN_SOURCE_GROUP, reinterpret_cast<const char *>(&group_source_req),
+ sizeof(group_source_req));
+#endif
+
+#if HAVE_DECL_IP_ADD_SOURCE_MEMBERSHIP
+#ifdef HAVE_STRUCT_IP_MREQ_SOURCE
+ // Some operating systems will have MCAST_JOIN_SOURCE_GROUP but still fail
+ // In those cases try the IP_ADD_SOURCE_MEMBERSHIP
+ if (rc < 0) {
+#ifdef HAVE_STRUCT_IP_MREQ_SOURCE_IMR_MULTIADDR_S_ADDR
+ imr.imr_multiaddr = ((const struct sockaddr_in *)group)->sin_addr;
+ imr.imr_sourceaddr = ((const struct sockaddr_in *)source)->sin_addr;
+#else
+ // Some Android versions declare mreq_source without an s_addr
+ imr.imr_multiaddr = ((const struct sockaddr_in *)group)->sin_addr.s_addr;
+ imr.imr_sourceaddr = ((const struct sockaddr_in *)source)->sin_addr.s_addr;
+#endif
+ rc = setsockopt (ListenSocket, IPPROTO_IP, IP_ADD_SOURCE_MEMBERSHIP, reinterpret_cast<char*>(&imr), sizeof (imr));
+ }
+#endif
+#endif
+ FAIL_errno(rc == SOCKET_ERROR, "mcast join source group",mSettings);
+ } else {
+ struct group_req group_req;
+ struct sockaddr_in *group;
+
+ memset(&group_req, 0, sizeof(struct group_req));
+
+ group_req.gr_interface = iface;
+ group=reinterpret_cast<struct sockaddr_in*>(&group_req.gr_group);
+ group->sin_family = AF_INET;
+ /* Set the group */
+ rc=getsockname(ListenSocket,reinterpret_cast<struct sockaddr *>(group), &socklen);
+ FAIL_errno(rc == SOCKET_ERROR, "mcast join group getsockname",mSettings);
+ group->sin_port = 0; /* Ignored */
+ rc = -1;
+#if HAVE_DECL_MCAST_JOIN_GROUP
+ rc = setsockopt(ListenSocket,IPPROTO_IP,MCAST_JOIN_GROUP, reinterpret_cast<const char *>(&group_req),
+ sizeof(group_source_req));
+#endif
+ FAIL_errno(rc == SOCKET_ERROR, "mcast join group",mSettings);
+ }
+ }
+
+#else
+ fprintf(stderr, "Unfortunately, SSM is not supported on this platform\n");
+ exit(-1);
+#endif
+ }
+}
+// end my_multicast_join()
+
+bool Listener::L2_setup (thread_Settings *server, int sockfd) {
+#if defined(HAVE_LINUX_FILTER_H) && defined(HAVE_AF_PACKET)
+ //
+ // Supporting parallel L2 UDP threads is a bit tricky. Below are some notes as to why and the approach used.
+ //
+ // The primary issues for UDP are:
+ //
+ // 1) We want the listener thread to hand off the flow to a server thread and not be burdened by that flow
+ // 2) For -P support, the listener thread neads to detect new flows which will share the same UDP port
+ // and UDP is stateless
+ //
+ // The listener thread needs to detect new traffic flows and hand them to a new server thread, and then
+ // rehang a listen/accept. For standard iperf the "flow routing" is done using connect() per the ip quintuple.
+ // The OS will then route established connected flows to the socket descriptor handled by a server thread and won't
+ // burden the listener thread with these packets.
+ //
+ // For L2 verification, we have to create a two sockets that will exist for the life of the flow. A
+ // new packet socket (AF_PACKET) will receive L2 frames and bypasses
+ // the OS network stack. The original AF_INET socket will still send up packets
+ // to the network stack.
+ //
+ // When using packet sockets there is inherent packet duplication, the hand off to a server
+ // thread is not so straight forward as packets will continue being sent up to the listener thread
+ // (technical problem is that packet sockets do not support connect() which binds the IP quintuple as the
+ // forwarding key) Since the Listener uses recvfrom(), there is no OS mechanism to detect new flows nor
+ // to drop packets. The listener can't listen on quintuple based connected flows because the client's source
+ // port is unknown. Therefore the Listener thread will continue to receive packets from all established
+ // flows sharing the same dst port which will impact CPU utilization and hence performance.
+ //
+ // The technique used to address this is to open an AF_PACKET socket and leave the AF_INET socket open.
+ // (This also aligns with BSD based systems) The original AF_INET socket will remain in the (connected)
+ // state so the network stack has it's connected state. A cBPF is then used to cause the kernel to fast drop
+ // those packets. A cBPF is set up to drop such packets. The test traffic will then only come over the
+ // packet (raw) socket and not the AF_INET socket. If we were to try to close the original AF_INET socket
+ // (vs leave it open w/the fast drop cBPF) then the existing traffic will be sent up by the network stack
+ // to he Listener thread, flooding it with packets, again something we want to avoid.
+ //
+ // On the packet (raw) socket itself, we do two more things to better handle performance
+ //
+ // 1) Use a full quintuple cBPF allowing the kernel to filter packets (allow) per the quintuple
+ // 2) Use the packet fanout option to assign a CBPF to a socket and hence to a single server thread minimizing
+ // duplication (reduce all cBPF's filtering load)
+ //
+ struct sockaddr *p = reinterpret_cast<sockaddr *>(&server->peer);
+ struct sockaddr *l = reinterpret_cast<sockaddr *>(&server->local);
+ int rc = 0;
+
+ //
+ // Establish a packet (raw) socket to be used by the server thread giving it full L2 packets
+ //
+ struct sockaddr s;
+ socklen_t len = sizeof(s);
+ getpeername(sockfd, &s, &len);
+ if (isIPV6(server)) {
+ server->mSock = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_IPV6));
+ WARN_errno(server->mSock == INVALID_SOCKET, "ip6 packet socket (AF_PACKET)");
+ server->l4offset = IPV6HDRLEN + sizeof(struct ether_header);
+ } else {
+ server->mSock = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_IP));
+ WARN_errno(server->mSock == INVALID_SOCKET, "ip packet socket (AF_PACKET)");
+ unsetIPV6(server);
+ server->l4offset = sizeof(struct iphdr) + sizeof(struct ether_header);
+ }
+ // Didn't get a valid socket, return now
+ if (server->mSock < 0) {
+ return false;
+ }
+ // More per thread settings based on using a packet socket
+ server->l4payloadoffset = server->l4offset + sizeof(struct udphdr);
+ server->recvflags = MSG_TRUNC;
+ // The original AF_INET socket only exists to keep the connected state
+ // in the OS for this flow. Fast drop packets there as
+ // now packets will use the AF_PACKET (raw) socket
+ // Also, store the original AF_INET socket descriptor so it can be
+ // closed in the Server's destructor. (Note: closing the
+ // socket descriptors will also free the cBPF.)
+ //
+ server->mSockDrop = sockfd;
+ rc = SockAddr_Drop_All_BPF(sockfd);
+ WARN_errno(rc == SOCKET_ERROR, "l2 all drop bpf");
+
+ // Now optimize packet flow up the raw socket
+ // Establish the flow BPF to forward up only "connected" packets to this raw socket
+ if (l->sa_family == AF_INET6) {
+#if HAVE_IPV6
+ struct in6_addr *v6peer = SockAddr_get_in6_addr(&server->peer);
+ struct in6_addr *v6local = SockAddr_get_in6_addr(&server->local);
+ if (isIPV6(server)) {
+ rc = SockAddr_v6_Connect_BPF(server->mSock, v6local, v6peer, (reinterpret_cast<struct sockaddr_in6 *>(l))->sin6_port, (reinterpret_cast<struct sockaddr_in6 *>(p))->sin6_port);
+ WARN_errno(rc == SOCKET_ERROR, "l2 connect ipv6 bpf");
+ } else {
+ // This is an ipv4 address in a v6 family (structure), just pull the lower 32 bits for the v4 addr
+ rc = SockAddr_v4_Connect_BPF(server->mSock, v6local->s6_addr32[3], v6peer->s6_addr32[3], (reinterpret_cast<struct sockaddr_in6 *>(l))->sin6_port, (reinterpret_cast<struct sockaddr_in6 *>(p))->sin6_port);
+ WARN_errno(rc == SOCKET_ERROR, "l2 v4in6 connect ip bpf");
+ }
+#else
+ fprintf(stderr, "Unfortunately, IPv6 is not supported on this platform\n");
+ return false;
+#endif /* HAVE_IPV6 */
+ } else {
+ rc = SockAddr_v4_Connect_BPF(server->mSock, (reinterpret_cast<struct sockaddr_in *>(l))->sin_addr.s_addr, (reinterpret_cast<struct sockaddr_in *>(p))->sin_addr.s_addr, (reinterpret_cast<struct sockaddr_in *>(l))->sin_port, (reinterpret_cast<struct sockaddr_in *>(p))->sin_port);
+ WARN_errno(rc == SOCKET_ERROR, "l2 connect ip bpf");
+ }
+ return rc >= 0;
+#else
+ fprintf(stderr, "Client requested --l2checks but not supported on this platform\n");
+ return false;
+#endif
+}
+
+bool Listener::tap_setup (thread_Settings *server, int sockfd) {
+#if defined(HAVE_IF_TUNTAP) && defined(HAVE_AF_PACKET) && defined(HAVE_DECL_SO_BINDTODEVICE)
+ struct sockaddr *p = reinterpret_cast<sockaddr *>(&server->peer);
+ struct sockaddr *l = reinterpret_cast<sockaddr *>(&server->local);
+ int rc = 0;
+
+ //
+ // Establish a packet (raw) socket to be used by the server thread giving it full L2 packets
+ //
+ struct sockaddr s;
+ socklen_t len = sizeof(s);
+ getpeername(sockfd, &s, &len);
+ if (isIPV6(server)) {
+ server->l4offset = IPV6HDRLEN + sizeof(struct ether_header);
+ } else {
+ server->l4offset = sizeof(struct iphdr) + sizeof(struct ether_header);
+ }
+ // Didn't get a valid socket, return now
+ if (server->mSock < 0) {
+ return false;
+ }
+ // More per thread settings based on using a packet socket
+ server->l4payloadoffset = server->l4offset + sizeof(struct udphdr);
+ server->recvflags = MSG_TRUNC;
+ // Now optimize packet flow up the raw socket
+ // Establish the flow BPF to forward up only "connected" packets to this raw socket
+ if (l->sa_family == AF_INET6) {
+#if HAVE_IPV6
+ struct in6_addr *v6peer = SockAddr_get_in6_addr(&server->peer);
+ struct in6_addr *v6local = SockAddr_get_in6_addr(&server->local);
+ if (isIPV6(server)) {
+ rc = SockAddr_v6_Connect_BPF(server->mSock, v6local, v6peer, (reinterpret_cast<struct sockaddr_in6 *>(l))->sin6_port, (reinterpret_cast<struct sockaddr_in6 *>(p))->sin6_port);
+ WARN_errno(rc == SOCKET_ERROR, "l2 connect ipv6 bpf");
+ } else {
+ // This is an ipv4 address in a v6 family (structure), just pull the lower 32 bits for the v4 addr
+ rc = SockAddr_v4_Connect_BPF(server->mSock, v6local->s6_addr32[3], v6peer->s6_addr32[3], (reinterpret_cast<struct sockaddr_in6 *>(l))->sin6_port, (reinterpret_cast<struct sockaddr_in6 *>(p))->sin6_port);
+ WARN_errno(rc == SOCKET_ERROR, "l2 v4in6 connect ip bpf");
+ }
+#else
+ fprintf(stderr, "Unfortunately, IPv6 is not supported on this platform\n");
+ return false;
+#endif /* HAVE_IPV6 */
+ } else {
+ rc = SockAddr_v4_Connect_BPF(server->mSock, (reinterpret_cast<struct sockaddr_in *>(l))->sin_addr.s_addr, (reinterpret_cast<struct sockaddr_in *>(p))->sin_addr.s_addr, (reinterpret_cast<struct sockaddr_in *>(l))->sin_port, (reinterpret_cast<struct sockaddr_in *>(p))->sin_port);
+ WARN_errno(rc == SOCKET_ERROR, "l2 connect ip bpf");
+ }
+ return rc >= 0;
+#else
+ fprintf(stderr, "Client requested --l2checks but not supported on this platform\n");
+ return false;
+#endif
+}
+
+/* ------------------------------------------------------------------------
+ * Do the equivalent of an accept() call for UDP sockets. This checks
+ * a listening UDP socket for new or first received datagram
+ * ------------------------------------------------------------------- ----*/
+int Listener::udp_accept (thread_Settings *server) {
+ assert(server != NULL);
+ int nread = 0;
+ assert(ListenSocket > 0);
+ // Preset the server socket to INVALID, hang recvfrom on the Listener's socket
+ // The INVALID socket is used to keep the while loop going
+ server->mSock = INVALID_SOCKET;
+ intmax_t packetID;
+ struct UDP_datagram* mBuf_UDP = reinterpret_cast<struct UDP_datagram*>(server->mBuf);
+ // Look for a UDP packet with a postive seq no while draining any neg seq no packets
+ // The UDP client traffic thread uses negative seq numbers to indicate to the server that
+ // traffic is over. Some of those "final" packets can be in the stack/network pipeline after the server
+ // thread has ended and closed its reporting. The Listener will now receive them as if they are
+ // "first packets. Any "new" packets seen by the Listener causes a new "udp accept"
+ // per UDP's stateless design. So, in the case of negative seq nos, we know that this is
+ // most likely not a new client thread requiring a new server thread, but remnants of an
+ // old one that already ended. Hence, the Listener should ignore "first packets" when
+ // they have negative seq numbers.
+ do {
+ packetID = 0;
+ nread = recvfrom(ListenSocket, server->mBuf, server->mBufLen, 0, \
+ reinterpret_cast<struct sockaddr*>(&server->peer), &server->size_peer);
+ if (nread > 0) {
+ // filter and ignore negative sequence numbers, these can be heldover from a previous run
+ if (isSeqNo64b(mSettings)) {
+ // New client - Signed PacketID packed into unsigned id2,id
+ packetID = (static_cast<uint32_t>(ntohl(mBuf_UDP->id))) | (static_cast<uintmax_t>(ntohl(mBuf_UDP->id2)) << 32);
+ } else {
+ // Old client - Signed PacketID in Signed id
+ packetID = static_cast<int32_t>(ntohl(mBuf_UDP->id));
+ }
+ }
+ } while ((nread > 0) && (packetID < 0) && !sInterupted);
+ FAIL_errno(nread == SOCKET_ERROR, "recvfrom", mSettings);
+ if ((nread > 0) && !sInterupted) {
+ Timestamp now;
+ server->accept_time.tv_sec = now.getSecs();
+ server->accept_time.tv_usec = now.getUsecs();
+#if HAVE_THREAD_DEBUG
+ {
+ char tmpaddr[200];
+ size_t len=200;
+ unsigned short port = SockAddr_getPort(&server->peer);
+ SockAddr_getHostAddress(&server->peer, tmpaddr, len);
+ thread_debug("rcvfrom peer: %s port %d len=%d", tmpaddr, port, nread);
+ }
+#endif
+ // Handle connection for UDP sockets
+ int gid = Iperf_push_host_port_conditional(server);
+#if HAVE_THREAD_DEBUG
+ if (gid < 0)
+ thread_debug("rcvfrom peer: drop duplicate");
+#endif
+ if (gid > 0) {
+ int rc;
+ // We have a new UDP flow (based upon key of quintuple)
+ // so let's hand off this socket
+ // to the server and create a new listener socket
+ server->mSock = ListenSocket;
+ ListenSocket = INVALID_SOCKET;
+ // This connect() will allow the OS to only
+ // send packets with the ip quintuple up to the server
+ // socket and, hence, to the server thread (yet to be created)
+ // This connect() routing is only supported with AF_INET or AF_INET6 sockets,
+ // e.g. AF_PACKET sockets can't do this. We'll handle packet sockets later
+ // All UDP accepts here will use AF_INET. This is intentional and needed
+ rc = connect(server->mSock, reinterpret_cast<struct sockaddr*>(&server->peer), server->size_peer);
+ FAIL_errno(rc == SOCKET_ERROR, "connect UDP", mSettings);
+ server->size_local = sizeof(iperf_sockaddr);
+ getsockname(server->mSock, reinterpret_cast<sockaddr*>(&server->local), &server->size_local);
+ SockAddr_Ifrname(server);
+ server->firstreadbytes = nread;
+ }
+ }
+ return server->mSock;
+}
+
+
+#if (((HAVE_TUNTAP_TUN) || (HAVE_TUNTAP_TAP)) && (AF_PACKET))
+int Listener::tuntap_accept(thread_Settings *server) {
+ int rc = recv(server->mSock, server->mBuf, (server->mBufLen + TAPBYTESSLOP + sizeof(struct iphdr) + sizeof(struct ether_header) + sizeof(struct udphdr)), 0);
+ if (rc <= 0)
+ return 0;
+// rc = udpchecksum((void *)ip_hdr, (void *)udp_hdr, udplen, (isIPV6(mSettings) ? 1 : 0));
+ struct iphdr *l3hdr = (struct iphdr *)((char *)server->mBuf + sizeof(struct ether_header));
+ struct udphdr *l4hdr = (struct udphdr *)((char *)server->mBuf + sizeof(struct iphdr) + sizeof(struct ether_header));
+// uint16_t ipver = (uint16_t) ntohs(mBuf + sizeof(struct ether_header));
+// printf ("*** version = %d\n", ipver);
+ // Note: sockaddrs are stored in network bytes order
+ struct sockaddr_in *local = (struct sockaddr_in *) &server->local;
+ struct sockaddr_in *peer = (struct sockaddr_in *) &server->peer;
+ server->size_peer = sizeof(iperf_sockaddr);
+ server->size_local = sizeof(iperf_sockaddr);
+ peer->sin_family = AF_INET;
+ local->sin_family = AF_INET;
+ peer->sin_addr.s_addr = l3hdr->saddr;
+ local->sin_addr.s_addr = l3hdr->daddr;
+ peer->sin_port = l4hdr->source;
+ local->sin_port = l4hdr->dest;
+ server->l4offset = sizeof(struct iphdr) + sizeof(struct ether_header);
+ SockAddr_v4_Connect_TAP_BPF(server->mSock, local->sin_addr.s_addr, peer->sin_addr.s_addr, local->sin_port, peer->sin_port);
+ server->l4payloadoffset = sizeof(struct iphdr) + sizeof(struct ether_header) + sizeof(struct udphdr);
+ server->firstreadbytes = rc;
+ return server->mSock;
+}
+#endif
+/* -------------------------------------------------------------------
+ * This is called by the Listener thread main loop, return a socket or error
+ * ------------------------------------------------------------------- */
+int Listener::my_accept (thread_Settings *server) {
+ assert(server != NULL);
+#ifdef HAVE_THREAD_DEBUG
+ if (isUDP(server)) {
+ thread_debug("Listener thread listening for UDP (sock=%d)", ListenSocket);
+ } else {
+ thread_debug("Listener thread listening for TCP (sock=%d)", ListenSocket);
+ }
+#endif
+ SockAddr_zeroAddress(&server->peer);
+ server->size_peer = sizeof(iperf_sockaddr);
+ server->accept_time.tv_sec = 0;
+ server->accept_time.tv_usec = 0;
+ if (isUDP(server)) {
+#if (((HAVE_TUNTAP_TUN) || (HAVE_TUNTAP_TAP)) && (AF_PACKET))
+ if (isTapDev(server) || isTunDev(server)) {
+ server->mSock = tuntap_accept(server);
+ } else
+#endif
+ {
+ server->mSock = udp_accept(server);
+ }
+ // note udp_accept will update the active host table
+ } else {
+ // accept a TCP connection
+ server->mSock = accept(ListenSocket, reinterpret_cast<sockaddr*>(&server->peer), &server->size_peer);
+ if (server->mSock > 0) {
+ Timestamp now;
+ server->accept_time.tv_sec = now.getSecs();
+ server->accept_time.tv_usec = now.getUsecs();
+ server->size_local = sizeof(iperf_sockaddr);
+ getsockname(server->mSock, reinterpret_cast<sockaddr*>(&server->local), &server->size_local);
+ SockAddr_Ifrname(server);
+ Iperf_push_host(server);
+ }
+ }
+ return server->mSock;
+} // end my_accept
+
+// Read deep enough into the packet to get the client settings
+// Read the headers but don't pull them from the queue in order to
+// preserve server thread accounting, i.e. these exchanges will
+// be part of traffic accounting. Return false if it's determined
+// this traffic shouldn't be accepted for a test run
+// Description of bits and fields is in include/payloads.h
+bool Listener::apply_client_settings (thread_Settings *server) {
+ assert(server != NULL);
+ bool rc = false;
+
+ // Set the receive timeout for the very first read
+ int sorcvtimer = TESTEXCHANGETIMEOUT; // 4 sec in usecs
+ SetSocketOptionsReceiveTimeout(server, sorcvtimer);
+ server->peer_version_u = 0;
+ server->peer_version_l = 0;
+ server->mMode = kTest_Normal;
+
+ if (isUDP(server)) {
+ rc = apply_client_settings_udp(server);
+ } else if (!isConnectOnly(server)) {
+ rc = apply_client_settings_tcp(server);
+ }
+ if (isOverrideTOS(server)) {
+ SetSocketOptionsIPTos(server, server->mRTOS);
+ } else if (server->mTOS) {
+ SetSocketOptionsIPTos(server, server->mTOS);
+ }
+ return rc;
+}
+
+inline bool Listener::test_permit_key(uint32_t flags, thread_Settings *server, int keyoffset) {
+ if (!(flags & HEADER_KEYCHECK)) {
+ server->mKeyCheck= false;
+ return false;
+ }
+ struct permitKey *thiskey = reinterpret_cast<struct permitKey *>(server->mBuf + (keyoffset - sizeof(thiskey->length)));
+ int keylen = ntohs(thiskey->length);
+ if ((keylen < MIN_PERMITKEY_LEN) || (keylen > MAX_PERMITKEY_LEN)) {
+ server->mKeyCheck= false;
+// fprintf(stderr, "REJECT: key length bounds error (%d)\n", keylen);
+ return false;
+ }
+ if (keylen != static_cast<int>(strlen(server->mPermitKey))) {
+ server->mKeyCheck= false;
+// fprintf(stderr, "REJECT: key length mismatch (%d!=%d)\n", keylen, (int) strlen(server->mPermitKey));
+ return false;
+ }
+ if (!isUDP(server)) {
+ int nread = 0;
+ nread = recvn(server->mSock, reinterpret_cast<char *>(&thiskey->value), keylen, 0);
+ FAIL_errno((nread < keylen), "read key", server);
+ }
+ strncpy(server->mPermitKey, thiskey->value, MAX_PERMITKEY_LEN + 1);
+ if (strncmp(server->mPermitKey, mSettings->mPermitKey, keylen) != 0) {
+ server->mKeyCheck= false;
+// fprintf(stderr, "REJECT: key value mismatch per %s\n", thiskey->value);
+ return false;
+ }
+ server->mKeyCheck= true;
+ return true;
+}
+
+bool Listener::apply_client_settings_udp (thread_Settings *server) {
+ struct client_udp_testhdr *hdr = reinterpret_cast<struct client_udp_testhdr *>(server->mBuf + server->l4payloadoffset);
+ uint32_t flags = ntohl(hdr->base.flags);
+ uint16_t upperflags = 0;
+ if (flags & HEADER_SEQNO64B) {
+ setSeqNo64b(server);
+ }
+#if HAVE_THREAD_DEBUG
+ thread_debug("UDP test flags = %X", flags);
+#endif
+ if (flags & HEADER32_SMALL_TRIPTIMES) {
+#if HAVE_THREAD_DEBUG
+ thread_debug("UDP small header");
+#endif
+ server->sent_time.tv_sec = ntohl(hdr->seqno_ts.tv_sec);
+ server->sent_time.tv_usec = ntohl(hdr->seqno_ts.tv_usec);
+ uint32_t seqno = ntohl(hdr->seqno_ts.id);
+ if (seqno != 1) {
+ fprintf(stderr, "WARN: first received packet (id=%d) was not first sent packet, report start time will be off\n", seqno);
+ }
+ Timestamp now;
+ if (!isTxStartTime(server) && ((abs(now.getSecs() - server->sent_time.tv_sec)) > (MAXDIFFTIMESTAMPSECS + 1))) {
+ fprintf(stdout,"WARN: ignore --trip-times because client didn't provide valid start timestamp within %d seconds of now\n", MAXDIFFTIMESTAMPSECS);
+ unsetTripTime(server);
+ } else {
+ setTripTime(server);
+ }
+ setEnhanced(server);
+ } else if ((flags & HEADER_VERSION1) || (flags & HEADER_VERSION2) || (flags & HEADER_EXTEND)) {
+ if ((flags & HEADER_VERSION1) && !(flags & HEADER_VERSION2)) {
+ if (flags & RUN_NOW)
+ server->mMode = kTest_DualTest;
+ else
+ server->mMode = kTest_TradeOff;
+ }
+ if (flags & HEADER_EXTEND) {
+ upperflags = htons(hdr->extend.upperflags);
+ server->mTOS = ntohs(hdr->extend.tos);
+ server->peer_version_u = ntohl(hdr->extend.version_u);
+ server->peer_version_l = ntohl(hdr->extend.version_l);
+ if (flags & HEADER_UDPTESTS) {
+ // Handle stateless flags
+ if (upperflags & HEADER_ISOCH) {
+ setIsochronous(server);
+ }
+ if (upperflags & HEADER_L2ETHPIPV6) {
+ setIPV6(server);
+ } else {
+ unsetIPV6(server);
+ }
+ if (upperflags & HEADER_L2LENCHECK) {
+ setL2LengthCheck(server);
+ }
+ if (upperflags & HEADER_NOUDPFIN) {
+ setNoUDPfin(server);
+ }
+ }
+ if (upperflags & HEADER_EPOCH_START) {
+ server->txstart_epoch.tv_sec = ntohl(hdr->start_fq.start_tv_sec);
+ server->txstart_epoch.tv_usec = ntohl(hdr->start_fq.start_tv_usec);
+ Timestamp now;
+ if ((abs(now.getSecs() - server->txstart_epoch.tv_sec)) > (MAXDIFFTXSTART + 1)) {
+ fprintf(stdout,"WARN: ignore --txstart-time because client didn't provide valid start timestamp within %d seconds of now\n", MAXDIFFTXSTART);
+ unsetTxStartTime(server);
+ } else {
+ setTxStartTime(server);
+ }
+ }
+ if (upperflags & HEADER_TRIPTIME) {
+ server->sent_time.tv_sec = ntohl(hdr->start_fq.start_tv_sec);
+ server->sent_time.tv_usec = ntohl(hdr->start_fq.start_tv_usec);
+ Timestamp now;
+ if (!isTxStartTime(server) && ((abs(now.getSecs() - server->sent_time.tv_sec)) > (MAXDIFFTIMESTAMPSECS + 1))) {
+ fprintf(stdout,"WARN: ignore --trip-times because client didn't provide valid start timestamp within %d seconds of now\n", MAXDIFFTIMESTAMPSECS);
+ unsetTripTime(server);
+ } else {
+ setTripTime(server);
+ setEnhanced(server);
+ }
+ }
+ }
+ if (flags & HEADER_VERSION2) {
+ upperflags = htons(hdr->extend.upperflags);
+ if (upperflags & HEADER_FULLDUPLEX) {
+ setFullDuplex(server);
+ setServerReverse(server);
+ }
+ if (upperflags & HEADER_REVERSE) {
+ server->mThreadMode=kMode_Client;
+ setServerReverse(server);
+ if (server->mSumReport != NULL) {
+ server->mSumReport->info.common->ThreadMode=kMode_Client;
+ setServerReverse(server->mSumReport->info.common);
+ }
+ setNoUDPfin(server);
+ unsetReport(server);
+ }
+ }
+ }
+ return true;
+}
+bool Listener::apply_client_settings_tcp (thread_Settings *server) {
+ bool rc = false;
+#if HAVE_TCP_STATS
+ if (!isUDP(mSettings)) {
+ gettcpinfo(server->mSock, &server->tcpinitstats);
+ }
+#endif
+ int nread = recvn(server->mSock, server->mBuf, sizeof(uint32_t), 0);
+ char *readptr = server->mBuf;
+ if (nread == 0) {
+ //peer closed the socket, with no writes e.g. a connect-only test
+ WARN(1, "read tcp flags (peer close)");
+ goto DONE;
+ }
+ if (nread < (int) sizeof(uint32_t)) {
+ WARN(1, "read tcp flags (runt)");
+ goto DONE;
+ } else {
+ rc = true;
+ readptr += nread;
+ struct client_tcp_testhdr *hdr = reinterpret_cast<struct client_tcp_testhdr *>(server->mBuf);
+ uint32_t flags = ntohl(hdr->base.flags);
+ if (flags & HEADER_BOUNCEBACK) {
+ if (!isServerModeTime(server)) {
+ unsetModeTime(server);
+ }
+ struct bounceback_hdr *bbhdr = reinterpret_cast<struct bounceback_hdr *>(server->mBuf);
+ setBounceBack(server);
+ nread = recvn(server->mSock, readptr, sizeof(struct bounceback_hdr), 0);
+ if (nread != sizeof(struct bounceback_hdr)) {
+ WARN(1, "read bounce back header failed");
+ rc = false;
+ goto DONE;
+ }
+ readptr += nread;
+ server->mBounceBackBytes = ntohl(bbhdr->bbsize);
+ server->mBounceBackHold = ntohl(bbhdr->bbhold);
+ uint16_t bbflags = ntohs(bbhdr->bbflags);
+ if (bbflags & HEADER_BBCLOCKSYNCED) {
+ setTripTime(server);
+ server->sent_time.tv_sec = ntohl(bbhdr->bbclientTx_ts.sec);
+ server->sent_time.tv_usec = ntohl(bbhdr->bbclientTx_ts.usec);
+ }
+ if (bbflags & HEADER_BBTOS) {
+ server->mTOS = ntohs(bbhdr->tos);
+ }
+#if HAVE_DECL_TCP_QUICKACK
+ if (bbflags & HEADER_BBQUICKACK) {
+ setTcpQuickAck(server);
+ }
+#endif
+ int remaining = server->mBounceBackBytes - (sizeof(struct bounceback_hdr) + sizeof(uint32_t));
+ if (remaining < 0) {
+ WARN(1, "bounce back bytes too small");
+ rc = false;
+ goto DONE;
+ } else if (remaining > 0) {
+ nread = recvn(server->mSock, readptr, remaining, 0);
+ if (nread != remaining) {
+ WARN(1, "read bounce back payload failed");
+ rc = false;
+ goto DONE;
+ }
+ }
+ Timestamp now;
+ bbhdr->bbserverRx_ts.sec = htonl(now.getSecs());
+ bbhdr->bbserverRx_ts.usec = htonl(now.getUsecs());
+ } else {
+ uint16_t upperflags = 0;
+ int readlen;
+ // figure out the length of the test header
+ if ((readlen = Settings_ClientTestHdrLen(flags, server)) > 0) {
+ // read the test settings passed to the server by the client
+ nread += recvn(server->mSock, readptr, (readlen - (int) sizeof(uint32_t)), 0);
+ FAIL_errno((nread < readlen), "read tcp test info", server);
+ if (isPermitKey(mSettings)) {
+ if (!test_permit_key(flags, server, readlen)) {
+ rc = false;
+ goto DONE;
+ }
+ } else if (flags & HEADER_KEYCHECK) {
+ rc = false;
+ server->mKeyCheck = false;
+ goto DONE;
+ }
+ server->firstreadbytes = nread;
+ struct client_tcp_testhdr *hdr = reinterpret_cast<struct client_tcp_testhdr*>(server->mBuf);
+ if ((flags & HEADER_VERSION1) && !(flags & HEADER_VERSION2)) {
+ if (flags & RUN_NOW)
+ server->mMode = kTest_DualTest;
+ else
+ server->mMode = kTest_TradeOff;
+ }
+ if (flags & HEADER_EXTEND) {
+ upperflags = htons(hdr->extend.upperflags);
+ server->mTOS = ntohs(hdr->extend.tos);
+ server->peer_version_u = ntohl(hdr->extend.version_u);
+ server->peer_version_l = ntohl(hdr->extend.version_l);
+ if (upperflags & HEADER_ISOCH) {
+ setIsochronous(server);
+ }
+ if (upperflags & HEADER_EPOCH_START) {
+ server->txstart_epoch.tv_sec = ntohl(hdr->start_fq.start_tv_sec);
+ server->txstart_epoch.tv_usec = ntohl(hdr->start_fq.start_tv_usec);
+ Timestamp now;
+ if ((abs(now.getSecs() - server->txstart_epoch.tv_sec)) > (MAXDIFFTXSTART + 1)) {
+ fprintf(stdout,"WARN: ignore --txstart-time because client didn't provide valid start timestamp within %d seconds of now\n", MAXDIFFTXSTART);
+ unsetTxStartTime(server);
+ } else {
+ setTxStartTime(server);
+ }
+ }
+ if (upperflags & HEADER_TRIPTIME) {
+ Timestamp now;
+ server->sent_time.tv_sec = ntohl(hdr->start_fq.start_tv_sec);
+ server->sent_time.tv_usec = ntohl(hdr->start_fq.start_tv_usec);
+ if (!isTxStartTime(server) && ((abs(now.getSecs() - server->sent_time.tv_sec)) > (MAXDIFFTIMESTAMPSECS + 1))) {
+ fprintf(stdout,"WARN: ignore --trip-times because client didn't provide valid start timestamp within %d seconds of now\n", MAXDIFFTIMESTAMPSECS);
+ unsetTripTime(server);
+ } else {
+ setTripTime(server);
+ setEnhanced(server);
+ }
+ }
+ if (upperflags & HEADER_PERIODICBURST) {
+ setEnhanced(server);
+ setFrameInterval(server);
+ setPeriodicBurst(server);
+ {
+ struct client_tcp_testhdr *hdr = reinterpret_cast<struct client_tcp_testhdr *>(server->mBuf);
+ server->mFPS = ntohl(hdr->isoch_settings.FPSl);
+ server->mFPS += ntohl(hdr->isoch_settings.FPSu) / static_cast<double>(rMillion);
+ }
+ if (!server->mFPS) {
+ server->mFPS = 1.0;
+ }
+ }
+ if (flags & HEADER_VERSION2) {
+ if (upperflags & HEADER_FULLDUPLEX) {
+ setFullDuplex(server);
+ setServerReverse(server);
+ }
+ if (upperflags & HEADER_REVERSE) {
+ server->mThreadMode=kMode_Client;
+ setServerReverse(server);
+ if (server->mSumReport != NULL) {
+ server->mSumReport->info.common->ThreadMode=kMode_Client;
+ setServerReverse(server->mSumReport->info.common);
+ }
+ }
+ }
+#if HAVE_DECL_TCP_NOTSENT_LOWAT
+ if ((isServerReverse(server) || isFullDuplex(server)) && (upperflags & HEADER_WRITEPREFETCH)) {
+ server->mWritePrefetch = ntohl(hdr->extend.TCPWritePrefetch);
+ if (server->mWritePrefetch > 0) {
+ setWritePrefetch(server);
+ }
+ }
+#endif
+#if HAVE_DECL_TCP_QUICKACK
+ if (upperflags & HEADER_TCPQUICKACK) {
+ setTcpQuickAck(server);
+ }
+#endif
+ if (upperflags & HEADER_BOUNCEBACK) {
+ setBounceBack(server);
+ }
+ }
+ }
+ }
+ // Handle case that requires an ack back to the client
+ // Signaled by not UDP (only supported by TCP)
+ // and either 2.0.13 flags or the newer 2.0.14 flag of
+ // V2PEERDETECT
+ if (!isUDP(server) && !isCompat(mSettings) && \
+ ((!(flags & HEADER_VERSION2) && (flags & HEADER_EXTEND)) || \
+ (flags & HEADER_V2PEERDETECT))) {
+ client_test_ack(server);
+ }
+ }
+ DONE:
+ return rc;
+}
+
+int Listener::client_test_ack(thread_Settings *server) {
+ if (isUDP(server))
+ return 1;
+
+ client_hdr_ack ack;
+ int sotimer = 0;
+ int size = sizeof(struct client_hdr_ack);
+ ack.typelen.type = htonl(CLIENTHDRACK);
+
+ ack.flags = 0;
+ ack.reserved1 = 0;
+ ack.reserved2 = 0;
+ ack.version_u = htonl(IPERF_VERSION_MAJORHEX);
+ ack.version_l = htonl(IPERF_VERSION_MINORHEX);
+ if (isTripTime(server)) {
+ ack.ts.sent_tv_sec = htonl(server->sent_time.tv_sec);
+ ack.ts.sent_tv_usec = htonl(server->sent_time.tv_usec);
+ ack.ts.sentrx_tv_sec = htonl(server->accept_time.tv_sec);
+ ack.ts.sentrx_tv_usec = htonl(server->accept_time.tv_usec);
+ Timestamp now;
+ ack.ts.ack_tv_sec = htonl(now.getSecs());
+ ack.ts.ack_tv_usec = htonl(now.getUsecs());
+ } else {
+ size -= sizeof (struct client_hdr_ack_ts);
+ }
+ ack.typelen.length = htonl(size);
+ int rc = 1;
+ // This is a version 2.0.10 or greater client
+ // write back to the client so it knows the server
+ // version
+
+ // sotimer units microseconds convert
+ if (server->mInterval) {
+ sotimer = static_cast<int>((server->mInterval) / 4);
+ } else if (isModeTime(server)) {
+ sotimer = static_cast<int>((server->mAmount * 10000) / 4);
+ }
+ if (sotimer > HDRXACKMAX) {
+ sotimer = HDRXACKMAX;
+ } else if (sotimer < HDRXACKMIN) {
+ sotimer = HDRXACKMIN;
+ }
+ SetSocketOptionsSendTimeout(server, sotimer);
+#if HAVE_DECL_TCP_NODELAY
+ int optflag = 1;
+ // Disable Nagle to reduce latency of this intial message
+ if ((rc = setsockopt(server->mSock, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<char *>(&optflag), sizeof(int))) < 0) {
+ WARN_errno(rc < 0, "tcpnodelay");
+ }
+#endif
+ if ((rc = send(server->mSock, reinterpret_cast<const char*>(&ack), size, 0)) < 0) {
+ WARN_errno(rc <= 0, "send_ack");
+ rc = 0;
+ }
+#if HAVE_DECL_TCP_NODELAY
+ // Re-nable Nagle
+ optflag = isNoDelay(server) ? 1 : 0;
+ if (!isUDP(server) && (rc = setsockopt(server->mSock, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<char *>(&optflag), sizeof(int))) < 0) {
+ WARN_errno(rc < 0, "tcpnodelay");
+ }
+#endif
+ return rc;
+}
diff --git a/src/Locale.c b/src/Locale.c
new file mode 100644
index 0000000..09333b0
--- /dev/null
+++ b/src/Locale.c
@@ -0,0 +1,757 @@
+/*---------------------------------------------------------------
+ * Copyright (c) 1999,2000,2001,2002,2003
+ * The Board of Trustees of the University of Illinois
+ * All Rights Reserved.
+ *---------------------------------------------------------------
+ * 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.
+ * ________________________________________________________________
+ * National Laboratory for Applied Network Research
+ * National Center for Supercomputing Applications
+ * University of Illinois at Urbana-Champaign
+ * http://www.ncsa.uiuc.edu
+ * ________________________________________________________________
+ *
+ * Locale.c
+ * by Ajay Tirumala <tirumala@ncsa.uiuc.edu>
+ * & Mark Gates <mgates@nlanr.net>
+ * -------------------------------------------------------------------
+ * Strings and other stuff that is locale specific.
+ * ------------------------------------------------------------------- */
+#include "headers.h"
+#include "version.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+/* -------------------------------------------------------------------
+ * usage
+ * ------------------------------------------------------------------- */
+
+const char usage_short[] = "\
+Usage: %s [-s|-c host] [options]\n\
+Try `%s --help' for more information.\n";
+
+const char usage_long1[] = "\
+Usage: iperf [-s|-c host] [options]\n\
+ iperf [-h|--help] [-v|--version]\n\
+\n\
+Client/Server:\n\
+ -b, --bandwidth #[kmgKMG | pps] bandwidth to read/send at in bits/sec or packets/sec\n\
+ -e, --enhanced use enhanced reporting giving more tcp/udp and traffic information\n\
+ -f, --format [kmgKMG] format to report: Kbits, Mbits, KBytes, MBytes\n\
+ --hide-ips hide ip addresses and host names within outputs\n\
+ -i, --interval # seconds between periodic bandwidth reports\n\
+ -l, --len #[kmKM] length of buffer in bytes to read or write (Defaults: TCP=128K, v4 UDP=1470, v6 UDP=1450)\n\
+ -m, --print_mss print TCP maximum segment size\n\
+ -o, --output <filename> output the report or error message to this specified file\n\
+ -p, --port # client/server port to listen/send on and to connect\n\
+ --permit-key permit key to be used to verify client and server (TCP only)\n\
+ --sum-only output sum only reports\n\
+ -u, --udp use UDP rather than TCP\n\
+ --utc use coordinated universal time (UTC) with time output\n\
+ -w, --window #[KM] TCP window size (socket buffer size)\n"
+#ifdef HAVE_SCHED_SETSCHEDULER
+" -z, --realtime request realtime scheduler\n"
+#endif
+" -B, --bind <host>[:<port>][%<dev>] bind to <host>, ip addr (including multicast address) and optional port and device\n\
+ -C, --compatibility for use with older versions does not sent extra msgs\n\
+ --NUM_REPORT_STRUCTS increase the shared memory between the traffic threads and the reporter thread (default is 10,000 entries)\n\
+ -M, --mss # set TCP maximum segment size using TCP_MAXSEG\n\
+ -N, --nodelay set TCP no delay, disabling Nagle's Algorithm\n\
+ -S, --tos # set the socket's IP_TOS (byte) field\n\
+ -Z, --tcp-congestion <algo> set TCP congestion control algorithm (Linux only)\n\
+\n\
+Server specific:\n\
+ -p, --port #[-#] server port(s) to listen on/connect to\n\
+ -s, --server run in server mode\n\
+ -1, --singleclient run one server at a time\n\
+ --histograms enable latency histograms\n\
+ --jitter-histograms enable jitter histograms\n\
+ --permit-key-timeout set the timeout for a permit key in seconds\n\
+ --tcp-rx-window-clamp set the TCP receive window clamp size in bytes\n\
+ --tap-dev #[<dev>] use TAP device to receive at L2 layer\n\
+ -t, --time # time in seconds to listen for new connections as well as to receive traffic (default not set)\n\
+ -B, --bind <ip>[%<dev>] bind to multicast address and optional device\n\
+ -U, --single_udp run in single threaded UDP mode\n\
+ --sum-dstip sum traffic threads based upon destination ip address (default is src ip)\n\
+ -D, --daemon run the server as a daemon\n"
+#ifdef WIN32
+" -R, --remove remove service in win32\n"
+#endif
+" -V, --ipv6_domain Enable IPv6 reception by setting the domain and socket to AF_INET6 (Can receive on both IPv4 and IPv6)\n"
+;
+
+const char usage_long2[] = "\
+\n\
+Client specific:\n\
+ --bounceback request a bounceback test (use -l for size, defaults to 100 bytes)\n\
+ --bounceback-hold request the server to insert a delay of n milliseconds between its read and write\n\
+ --bounceback-period request the client schedule a send every n milliseconds\n\
+ --bounceback-no-quickack request the server not set the TCP_QUICKACK socket option (disabling TCP ACK delays) during a bounceback test\n\
+ -c, --client <host> run in client mode, connecting to <host>\n\
+ --connect-only run a connect only test\n\
+ --connect-retries # number of times to retry tcp connect\n\
+ -d, --dualtest Do a bidirectional test simultaneously (multiple sockets)\n\
+ --fq-rate #[kmgKMG] bandwidth to socket pacing\n\
+ --full-duplex run full duplex test using same socket\n\
+ --ipg set the the interpacket gap (milliseconds) for packets within an isochronous frame\n\
+ --isochronous <frames-per-second>:<mean>,<stddev> send traffic in bursts (frames - emulate video traffic)\n\
+ --incr-dstip Increment the destination ip with parallel (-P) traffic threads\n\
+ --incr-dstport Increment the destination port with parallel (-P) traffic threads\n\
+ --incr-srcip Increment the source ip with parallel (-P) traffic threads\n\
+ --incr-srcport Increment the source port with parallel (-P) traffic threads\n\
+ --local-only Set don't route on socket\n\
+ --near-congestion=[w] Use a weighted write delay per the sampled TCP RTT (experimental)\n\
+ --no-connect-sync No sychronization after connect when -P or parallel traffic threads\n\
+ --no-udp-fin No final server to client stats at end of UDP test\n\
+ -n, --num #[kmgKMG] number of bytes to transmit (instead of -t)\n\
+ -r, --tradeoff Do a fullduplexectional test individually\n\
+ --tcp-quickack set the socket's TCP_QUICKACK option (off by default)\n\
+ --tcp-write-prefetch set the socket's TCP_NOTSENT_LOWAT value in bytes and use event based writes\n\
+ --tcp-write-times measure the socket write times at the application level\n\
+ -t, --time # time in seconds to transmit for (default 10 secs)\n\
+ --trip-times enable end to end measurements (requires client and server clock sync)\n\
+ --txdelay-time time in seconds to hold back after connect and before first write\n\
+ --txstart-time unix epoch time to schedule first write and start traffic\n\
+ -B, --bind [<ip> | <ip:port>] bind ip (and optional port) from which to source traffic\n\
+ -F, --fileinput <name> input the data to be transmitted from a file\n\
+ -H, --ssm-host <ip> set the SSM source, use with -B for (S,G) \n\
+ -I, --stdin input the data to be transmitted from stdin\n\
+ -L, --listenport # port to receive fullduplexectional tests back on\n\
+ -P, --parallel # number of parallel client threads to run\n"
+#ifndef WIN32
+" -R, --reverse reverse the test (client receives, server sends)\n"
+#else
+" -R Remove the windows service\n"
+" --reverse reverse the test (client receives, server sends)\n"
+#endif
+" -S, --tos IP DSCP or tos settings\n\
+ -T, --ttl # time-to-live, for multicast (default 1)\n\
+ --working-load request a concurrent TCP stream\n\
+ -V, --ipv6_domain Set the domain to IPv6 (send packets over IPv6)\n\
+ -X, --peer-detect perform server version detection and version exchange\n\
+\n\
+Miscellaneous:\n\
+ -x, --reportexclude [CDMSV] exclude C(connection) D(data) M(multicast) S(settings) V(server) reports\n\
+ -y, --reportstyle C report as a Comma-Separated Values\n\
+ -h, --help print this message and quit\n\
+ -v, --version print version information and quit\n\
+\n\
+[kmgKMG] Indicates options that support a k,m,g,K,M or G suffix\n\
+Lowercase format characters are 10^3 based and uppercase are 2^n based\n\
+(e.g. 1k = 1000, 1K = 1024, 1m = 1,000,000 and 1M = 1,048,576)\n\
+\n\
+Accepted tos values are: af11, af12, af13, af21, af22, af23, af31, af32, af33, af41, af42, af43,\n\
+cs0, cs1, cs2, cs3, cs4, cs5, cs6, cs7, ef, le, nqb, nqb2, ac_be, ac_bk, ac_vi, ac_vo, lowdelay, throughput,\n\
+reliability, or a numeric value.\n\
+\n\
+The TCP window size option can be set by the environment variable\n\
+TCP_WINDOW_SIZE. Most other options can be set by an environment variable\n\
+IPERF_<long option name>, such as IPERF_BANDWIDTH.\n\
+\n\
+Source at <http://sourceforge.net/projects/iperf2/>\n\
+Report bugs to <iperf-users@lists.sourceforge.net>\n";
+
+// include a description of the threading in the version
+#if defined( HAVE_POSIX_THREAD )
+ #define IPERF_THREADS "pthreads"
+#elif defined( HAVE_WIN32_THREAD )
+ #define IPERF_THREADS "win32 threads"
+#else
+ #define IPERF_THREADS "single threaded"
+#endif
+
+const char version[] =
+"iperf version " IPERF_VERSION " (" IPERF_VERSION_DATE ") " IPERF_THREADS "\n";
+
+/* -------------------------------------------------------------------
+ * settings
+ * ------------------------------------------------------------------- */
+
+const char separator_line[] =
+"------------------------------------------------------------\n";
+
+const char server_port[] =
+"Server listening on %s port %d\n";
+
+const char client_port[] =
+"Client connecting to %s, %s port %d\n";
+
+const char server_pid_port[] =
+"Server listening on %s port %d with pid %d\n";
+
+const char server_pid_portrange[] =
+"Server listening on %s ports %d-%d with pid %d\n";
+
+const char client_pid_port[] =
+"Client connecting to %s, %s port %d with pid %d (%d flows)\n";
+
+const char client_pid_port_dev[] =
+"Client connecting to %s, %s port %d with pid %d via %s (%d flows)\n";
+
+const char bind_address[] =
+"Binding to local address %s\n";
+
+const char bind_address_iface[] =
+"Binding to local address %s and iface %s\n";
+
+const char bind_address_iface_taptun[] =
+"Using virtual iface %s (AF_PACKET & SOCK_RAW)\n";
+
+const char multicast_ttl[] =
+"Setting multicast TTL to %d\n";
+
+const char join_multicast[] =
+"Joining multicast group %s\n";
+
+const char join_multicast_sg[] =
+"Joining multicast (S,G)=%s,%s\n";
+
+const char join_multicast_starg_dev[] =
+"Joining multicast (*,G)=*,%s w/iface %s\n";
+
+const char join_multicast_sg_dev[] =
+"Joining multicast (S,G)=%s,%s w/iface %s\n";
+
+const char client_datagram_size[] =
+"Sending %d byte datagrams, IPG target: %.2f us\n";
+
+const char client_datagram_size_kalman[] =
+"Sending %d byte datagrams, IPG target: %.2f us (kalman adjust)\n";
+
+const char server_datagram_size[] =
+"Receiving %d byte datagrams\n";
+
+const char tcp_window_size[] =
+"TCP window size";
+
+const char udp_buffer_size[] =
+"UDP buffer size";
+
+const char window_default[] =
+"(default)";
+
+const char wait_server_threads[] =
+"Waiting for server threads to complete. Interrupt again to force quit.\n";
+
+const char client_isochronous[] =
+"Isochronous: %0.2f frames/sec mean=%s/s, stddev=%s/s, Period/IPG=%0.2f/%.3f ms\n";
+
+const char client_burstperiod[] =
+"Bursting: %s every %0.2f second(s)\n";
+
+const char client_burstperiodcount[] =
+"Bursting: %s writes %d times every %0.2f second(s)\n";
+
+const char client_bounceback[] =
+"Bounce-back test (size=%s) (server hold req=%d usecs & tcp_quickack)\n";
+
+const char client_bounceback_noqack[] =
+"Bounce-back test (size=%s) (server hold req=%d usecs)\n";
+
+const char server_burstperiod[] =
+"Burst wait timeout set to (2 * %0.2f) seconds (use --burst-period=<n secs> to change)\n";
+
+const char client_fq_pacing [] =
+"fair-queue socket pacing set to %s/s\n";
+
+/* -------------------------------------------------------------------
+ * Legacy reports
+ * ------------------------------------------------------------------- */
+
+const char report_bw_header[] =
+"[ ID] Interval Transfer Bandwidth\n";
+
+const char report_bw_format[] =
+"%s" IPERFTimeFrmt " sec %ss %ss/sec\n";
+
+const char report_sum_bw_format[] =
+"[SUM] " IPERFTimeFrmt " sec %ss %ss/sec\n";
+
+const char report_sumcnt_bw_format[] =
+"[SUM-%d] " IPERFTimeFrmt " sec %ss %ss/sec\n";
+
+const char report_bw_jitter_loss_header[] =
+"[ ID] Interval Transfer Bandwidth Jitter Lost/Total Datagrams\n";
+
+const char report_bw_jitter_loss_format[] =
+"%s" IPERFTimeFrmt " sec %ss %ss/sec %6.3f ms %" PRIdMAX "/%" PRIdMAX " (%.2g%%)\n";
+
+const char report_sum_bw_jitter_loss_format[] =
+"[SUM] " IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "/%" PRIdMAX " (%.2g%%)\n";
+
+const char report_sumcnt_bw_jitter_loss_header[] =
+"[SUM-cnt] Interval Transfer Bandwidth Lost/Total PPS\n";
+
+const char report_sumcnt_bw_jitter_loss_format[] =
+"[SUM-%d] " IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "/%" PRIdMAX " %8.0f pps\n";
+
+/* -------------------------------------------------------------------
+ * Enhanced reports (per -e)
+ * ------------------------------------------------------------------- */
+const char report_sumcnt_bw_header[] =
+"[SUM-cnt] Interval Transfer Bandwidth\n";
+
+const char client_report_epoch_start_current[] =
+"[%3d] Client traffic to start in %d seconds at %s current time is %s\n";
+
+const char client_write_size[] =
+"Write buffer size";
+
+const char server_read_size[] =
+"Read buffer size";
+
+const char report_bw_enhanced_format[] =
+"%s" IPERFTimeFrmt " sec %ss %ss/sec\n";
+
+const char report_sum_bw_enhanced_format[] =
+"[SUM] " IPERFTimeFrmt " sec %ss %ss/sec\n";
+
+const char report_bw_read_enhanced_header[] =
+"[ ID] Interval" IPERFTimeSpace "Transfer Bandwidth Reads=Dist\n";
+
+const char report_bw_read_enhanced_format[] =
+"%s" IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "=%d:%d:%d:%d:%d:%d:%d:%d\n";
+
+const char report_sumcnt_bw_read_enhanced_header[] =
+"[SUM-cnt] Interval" IPERFTimeSpace "Transfer Bandwidth Reads=Dist\n";
+
+const char report_sumcnt_bw_read_enhanced_format[] =
+"[SUM-%d] " IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "=%d:%d:%d:%d:%d:%d:%d:%d\n";
+
+const char report_sumcnt_bw_read_triptime_header[] =
+"[SUM-cnt] Interval" IPERFTimeSpace "Transfer Bandwidth InP Reads=Dist\n";
+
+const char report_sumcnt_bw_read_triptime_format[] =
+"[SUM-%d] " IPERFTimeFrmt " sec %ss %ss/sec %s %" PRIdMAX "=%d:%d:%d:%d:%d:%d:%d:%d\n";
+
+const char report_bw_read_enhanced_netpwr_header[] =
+"[ ID] Interval" IPERFTimeSpace "Transfer Bandwidth Burst Latency avg/min/max/stdev (cnt/size) inP NetPwr Reads=Dist\n";
+
+const char report_bw_read_enhanced_netpwr_format[] =
+"%s" IPERFTimeFrmt " sec %ss %ss/sec %.3f/%.3f/%.3f/%.3f ms (%d/%d) %s %s %" PRIdMAX "=%d:%d:%d:%d:%d:%d:%d:%d\n";
+
+const char report_bw_isoch_enhanced_netpwr_header[] =
+"[ ID] Interval" IPERFTimeSpace "Transfer Bandwidth Burst Latency avg/min/max/stdev (cnt/size) NetPwr Reads=Dist\n";
+
+const char report_bw_isoch_enhanced_netpwr_format[] =
+"%s" IPERFTimeFrmt " sec %ss %ss/sec %.3f/%.3f/%.3f/%.3f ms (%d/%d) %s %" PRIdMAX "=%d:%d:%d:%d:%d:%d:%d:%d\n";
+
+const char report_sum_bw_read_enhanced_format[] =
+"[SUM] " IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "=%d:%d:%d:%d:%d:%d:%d:%d\n";
+
+const char report_triptime_enhanced_format[] =
+"%s" IPERFTimeFrmt " trip-time (3WHS done->fin+finack) = %.4f sec\n";
+
+#if HAVE_TCP_STATS
+const char report_client_bb_bw_header[] =
+"[ ID] Interval" IPERFTimeSpace "Transfer Bandwidth BB cnt=avg/min/max/stdev Rtry Cwnd/RTT RPS\n";
+
+const char report_client_bb_bw_format[] =
+"%s" IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "=%.3f/%.3f/%.3f/%.3f ms %4d %4dK/%u us %s rps\n";
+#else
+const char report_client_bb_bw_header[] =
+"[ ID] Interval" IPERFTimeSpace "Transfer Bandwidth BB cnt=avg/min/max/stdev RPS\n";
+
+const char report_client_bb_bw_format[] =
+"%s" IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "=%.3f/%.3f/%.3f/%.3f ms %s rps\n";
+#endif
+const char report_client_bb_bw_triptime_format[] =
+"%s" IPERFTimeFrmt " sec OWD Delays (ms) Cnt=%" PRIdMAX " To=%.3f/%.3f/%.3f/%.3f From=%.3f/%.3f/%.3f/%.3f Asymmetry=%.3f/%.3f/%.3f/%.3f %s rps\n";
+
+const char report_write_enhanced_write_header[] =
+"[ ID] Interval" IPERFTimeSpace "Transfer Bandwidth Write/Err Rtry Cwnd/RTT NetPwr write-times avg/min/max/stdev (cnt)\n";
+
+const char report_write_enhanced_write_format[] =
+"%s" IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "/%" PRIdMAX " %10" PRIdMAX "%8dK/%u us %s %.3f/%.3f/%.3f/%.3f ms (%" PRIdMAX ")\n";
+
+const char report_write_enhanced_nocwnd_write_format[] =
+"%s" IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "/%" PRIdMAX " %10" PRIdMAX " NA/%u us %s %.3f/%.3f/%.3f/%.3f ms (%" PRIdMAX ")\n";
+
+
+#if HAVE_TCP_STATS
+const char report_bw_write_enhanced_header[] =
+"[ ID] Interval" IPERFTimeSpace "Transfer Bandwidth Write/Err Rtry Cwnd/RTT(var) NetPwr\n";
+
+const char report_sum_bw_write_enhanced_format[] =
+"[SUM] " IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "/%" PRIdMAX "%10" PRIdMAX "\n";
+
+const char report_bw_write_enhanced_format[] =
+"%s" IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "/%" PRIdMAX "%10" PRIdMAX " %8dK/%u(%u) us %s\n";
+
+const char report_bw_write_enhanced_nocwnd_format[] =
+"%s" IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "/%" PRIdMAX " %10" PRIdMAX " NA/%u us %s\n";
+
+const char report_write_enhanced_isoch_header[] =
+"[ ID] Interval" IPERFTimeSpace "Transfer Bandwidth Write/Err Rtry Cwnd/RTT isoch:tx/miss/slip NetPwr\n";
+
+const char report_write_enhanced_isoch_format[] =
+"%s" IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "/%" PRIdMAX " %8d %8dK/%u us %9" PRIuMAX "/%" PRIuMAX "/%" PRIuMAX " %s\n";
+
+const char report_write_enhanced_isoch_nocwnd_format[] =
+"%s" IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "/%" PRIdMAX " %10" PRIdMAX " NA/%u us %9" PRIuMAX "/%" PRIuMAX "/%" PRIuMAX " %s\n";
+
+#else
+
+const char report_bw_write_enhanced_header[] =
+"[ ID] Interval" IPERFTimeSpace "Transfer Bandwidth Write/Err\n";
+
+const char report_bw_write_enhanced_format[] =
+"%s" IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "/%" PRIdMAX "\n";
+
+const char report_sum_bw_write_enhanced_format[] =
+"[SUM] " IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "/%" PRIdMAX "\n";
+
+const char report_write_enhanced_isoch_header[] =
+"[ ID] Interval" IPERFTimeSpace "Transfer Bandwidth Write/Err isoch:tx/miss/slip\n";
+
+const char report_write_enhanced_isoch_format[] =
+"%s" IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "/%" PRIdMAX "%9" PRIuMAX "/%" PRIuMAX "/%" PRIuMAX "\n";
+#endif
+
+const char report_sumcnt_bw_write_enhanced_header[] =
+"[SUM-cnt] Interval" IPERFTimeSpace "Transfer Bandwidth Write/Err Rtry\n";
+
+const char report_sumcnt_bw_write_enhanced_format[] =
+"[SUM-%d] " IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "/%" PRIdMAX "%10" PRIdMAX "\n";
+
+const char report_bw_pps_enhanced_header[] =
+"[ ID] Interval" IPERFTimeSpace "Transfer Bandwidth Write/Err PPS\n";
+
+const char report_bw_pps_enhanced_format[] =
+"%s" IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "/%" PRIdMAX " %8.0f pps\n";
+
+const char report_sumcnt_bw_pps_enhanced_header[] =
+"[SUM-cnt] Interval" IPERFTimeSpace "Transfer Bandwidth Write/Err PPS\n";
+
+const char report_sumcnt_bw_pps_enhanced_format[] =
+"[SUM-%d] " IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "/%" PRIdMAX " %8.0f pps\n";
+
+const char report_sumcnt_udp_enhanced_header[] =
+"[SUM-cnt] Interval" IPERFTimeSpace "Transfer Bandwidth Lost/Total PPS\n";
+
+const char report_sumcnt_udp_enhanced_format[] =
+"[SUM-%d] " IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "/%" PRIdMAX " %8.0f pps\n";
+
+const char report_sumcnt_udp_triptime_header[] =
+"[SUM-cnt] Interval" IPERFTimeSpace "Transfer Bandwidth Lost/Total Rx/inP PPS\n";
+
+const char report_sumcnt_udp_triptime_format[] =
+"[SUM-%d] " IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "/%" PRIdMAX " %" PRIdMAX "/%.0f %8.0f pps\n";
+
+const char report_bw_pps_enhanced_isoch_header[] =
+"[ ID] Interval" IPERFTimeSpace "Transfer Bandwidth Write/Err PPS isoch:tx/miss/slip\n";
+
+const char report_bw_pps_enhanced_isoch_format[] =
+"%s" IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "/%" PRIdMAX "%8.0f pps %3" PRIuMAX "/%" PRIuMAX "/%" PRIuMAX "\n";
+
+const char report_sum_bw_pps_enhanced_format[] =
+"[SUM] " IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "/%" PRIdMAX "%8.0f pps\n";
+
+const char report_bw_jitter_loss_pps_header[] =
+"[ ID] Interval Transfer Bandwidth Jitter Lost/Total Datagrams PPS\n";
+
+const char report_bw_jitter_loss_pps_format[] =
+"%s" IPERFTimeFrmt " sec %ss %ss/sec %6.3f ms %" PRIdMAX "/%" PRIdMAX " (%.2g%%) %8.0f pps\n";
+
+const char report_bw_jitter_loss_enhanced_header[] =
+"[ ID] Interval" IPERFTimeSpace "Transfer Bandwidth Jitter Lost/Total \
+ Latency avg/min/max/stdev PPS NetPwr\n";
+
+const char report_bw_jitter_loss_enhanced_format[] =
+"%s" IPERFTimeFrmt " sec %ss %ss/sec %6.3f ms %" PRIdMAX "/%" PRIdMAX " (%.2g%%) %.3f/%.3f/%.3f/%.3f ms %.0f pps %s\n";
+
+const char report_bw_jitter_loss_enhanced_triptime_header[] =
+"[ ID] Interval" IPERFTimeSpace "Transfer Bandwidth Jitter Lost/Total \
+ Latency avg/min/max/stdev PPS Rx/inP NetPwr\n";
+
+const char report_bw_jitter_loss_enhanced_triptime_format[] =
+"%s" IPERFTimeFrmt " sec %ss %ss/sec %6.3f ms %" PRIdMAX "/%" PRIdMAX " (%.2g%%) %.3f/%.3f/%.3f/%.3f ms %.0f pps %" PRIdMAX "/%s %s\n";
+
+const char report_bw_jitter_loss_enhanced_isoch_header[] =
+"[ ID] Interval" IPERFTimeSpace "Transfer Bandwidth Jitter Lost/Total \
+ Latency avg/min/max/stdev PPS NetPwr Isoch:rx/lost\n";
+
+const char report_bw_jitter_loss_enhanced_isoch_triptime_header[] =
+"[ ID] Interval" IPERFTimeSpace "Transfer Bandwidth Jitter Lost/Total \
+Latency avg/min/max/stdev PPS Frame:rx/lost Frame-latency avg/min/max/stdev NetPwr\n";
+
+const char report_bw_jitter_loss_enhanced_isoch_format[] =
+"%s" IPERFTimeFrmt " sec %ss %ss/sec %6.3f ms %" PRIdMAX "/%" PRIdMAX " (%.2g%%) %.3f/%.3f/%.3f/%.3f ms %.0f pps %3d/%d %.3f/%.3f/%.3f/%.3f ms %s\n";
+
+const char report_sum_bw_jitter_loss_enhanced_format[] =
+"[SUM] " IPERFTimeFrmt " sec %ss %ss/sec %6.3f ms %" PRIdMAX "/%" PRIdMAX " (%.2g%%) %.0f pps\n";
+
+const char report_sumcnt_bw_jitter_loss_enhanced_format[] =
+"[SUM-%d] " IPERFTimeFrmt " sec %ss %ss/sec %6.3f ms %" PRIdMAX "/%" PRIdMAX " (%.2g%%) %.0f pps\n";
+
+const char report_bw_jitter_loss_suppress_enhanced_format[] =
+"%s" IPERFTimeFrmt " sec %ss %ss/sec %6.3f ms %" PRIdMAX "/%" PRIdMAX " (%.2g%%) -/-/-/- ms %.0f pps\n";
+
+/*
+ * Frame interval reports
+ */
+#define IPERFFTimeFrmt "%4.4f-%4.4f"
+#define IPERFFTimeSpace " "
+const char report_frame_jitter_loss_enhanced_header[] =
+"[ ID] Interval(f-transit)" IPERFFTimeSpace "Transfer Bandwidth FrameID Jitter Lost/Total \
+ Latency avg/min/max/stdev PPS inP NetPwr\n";
+
+const char report_frame_jitter_loss_enhanced_format[] =
+"%s" IPERFFTimeFrmt "(%0.4f) sec %ss %ss/sec %" PRIdMAX " %6.3f ms %" PRIdMAX "/%" PRIdMAX " (%.2g%%) %.3f/%.3f/%.3f/%.3f ms %.0f pps %2.0f pkts %s\n";
+
+const char report_frame_jitter_loss_suppress_enhanced_format[] =
+"%s" IPERFTimeFrmt "(%0.4f) sec %" PRIdMAX " %ss %ss/sec %" PRIdMAX " %6.3f ms %" PRIdMAX "/%" PRIdMAX " (%.2g%%) -/-/-/- ms %.0f pps\n";
+
+const char report_frame_tcp_enhanced_header[] =
+"[ ID] Interval(f-transit)" IPERFFTimeSpace "Transfer Bandwidth FrameID\n";
+
+const char report_burst_read_tcp_header[] =
+"[ ID] Burst (start-end)" IPERFFTimeSpace "Transfer Bandwidth XferTime (DC%) Reads=Dist NetPwr\n";
+
+const char report_burst_read_tcp_format[] =
+"%s" IPERFTimeFrmt " sec %ss %ss/sec %6.3f ms (%.2g%%) %" PRIdMAX "=%d:%d:%d:%d:%d:%d:%d:%d %s\n";
+
+const char report_burst_read_tcp_final_format[] =
+"%s" IPERFTimeFrmt " sec %ss %ss/sec %.3f/%.3f/%.3f/%.3f ms %" PRIdMAX "=%d:%d:%d:%d:%d:%d:%d:%d\n";
+#if HAVE_TCP_STATS
+const char report_burst_write_tcp_header[] =
+"[ ID] Burst (start-end)" IPERFFTimeSpace "Transfer Bandwidth XferTime Write/Err Rtry Cwnd/RTT NetPwr\n";
+
+const char report_burst_write_tcp_format[] =
+"%s" IPERFTimeFrmt " sec %ss %ss/sec %6.3f ms %" PRIdMAX "/%" PRIdMAX "%10" PRIdMAX "%8dK/%u %s\n";
+
+#else
+const char report_burst_write_tcp_header[] =
+"[ ID] Burst (start-end)" IPERFFTimeSpace "Transfer Bandwidth XferTime Write/Err\n";
+
+const char report_burst_write_tcp_format[] =
+"%s" IPERFTimeFrmt " sec %ss %ss/sec %6.3f ms %" PRIdMAX "/%" PRIdMAX"\n";
+#endif
+
+const char report_burst_write_tcp_nocwnd_format[] =
+"%s" IPERFTimeFrmt " sec %ss %ss/sec %6.3f ms %" PRIdMAX "/%" PRIdMAX "%10" PRIdMAX "NA/%u %s\n";
+
+const char report_burst_write_tcp_final_format[] =
+"%s" IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "=%d:%d:%d:%d:%d:%d:%d:%d\n";
+
+/* -------------------------------------------------------------------
+ * Fullduplex reports
+ * ------------------------------------------------------------------- */
+const char report_bw_sum_fullduplex_format[] =
+"%s" IPERFTimeFrmt " sec %ss %ss/sec\n";
+
+const char report_bw_sum_fullduplex_enhanced_format[] =
+"[FD%d] " IPERFTimeFrmt " sec %ss %ss/sec\n";
+
+const char report_udp_fullduplex_header[] =
+"[ ID] Interval Transfer Bandwidth Datagrams PPS\n";
+
+const char report_sumcnt_udp_fullduplex_header[] =
+"[SUM-cnt] Interval Transfer Bandwidth Datagrams PPS\n";
+
+const char report_sumcnt_udp_fullduplex_format[] =
+"[SUM-%d] " IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "%8.0f pps\n";
+
+const char report_udp_fullduplex_format[] =
+"%s" IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "%8.0f pps\n";
+
+const char report_udp_fullduplex_enhanced_format[] =
+"[FD%d] " IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "%8.0f pps\n";
+
+const char report_udp_fullduplex_sum_format[] =
+"[SUM] " IPERFTimeFrmt " sec %ss %ss/sec %" PRIdMAX "%8.0f pps\n";
+
+/* -------------------------------------------------------------------
+ * Misc reports
+ * ------------------------------------------------------------------- */
+const char report_outoforder[] =
+"%s" IPERFTimeFrmt " sec %d datagrams received out-of-order\n";
+
+const char report_sumcnt_outoforder[] =
+"[SUM-%d] " IPERFTimeFrmt " sec %d datagrams received out-of-order\n";
+
+const char report_l2statistics[] =
+"%s" IPERFTimeFrmt " sec L2 processing detected errors, total(length/checksum/unknown) = %" PRIdMAX "(%" PRIdMAX "/%" PRIdMAX "/%" PRIdMAX ")\n";
+
+const char report_sum_outoforder[] =
+"[SUM] " IPERFTimeFrmt " sec %d datagrams received out-of-order\n";
+
+const char report_peer [] =
+"%slocal %s port %u connected with %s port %u%s\n";
+
+const char report_peer_dev [] =
+"%slocal %s%%%s port %u connected with %s port %u%s\n";
+
+const char report_peer_fail [] =
+"[drop] local %s port %u connection with %s port %u%s (permit key fail)\n";
+
+const char report_mss_unsupported[] =
+"%sMSS and MTU size unknown (TCP_MAXSEG not supported)\n";
+
+const char report_default_mss[] =
+"MSS size %d bytes\n";
+
+const char report_mss[] =
+"MSS req size %d bytes (per TCP_MAXSEG)\n";
+
+const char report_datagrams[] =
+"[%3d] Sent %d datagrams\n";
+
+const char report_sumcnt_datagrams[] =
+"[SUM-%d] Sent %d datagrams\n";
+
+const char report_sum_datagrams[] =
+"[SUM] Sent %d datagrams\n";
+
+const char server_reporting[] =
+"[%3d] Server Report:\n";
+
+const char reportCSV_peer[] =
+"%s,%u,%s,%u";
+
+const char report_l2length_error[] =
+"%s" IPERFTimeFrmt " sec %d datagrams received out-of-order\n";
+
+/*-------------------------------------------------------------------
+ * CSV outputs
+ *------------------------------------------------------------------*/
+const char reportCSV_bw_format[] =
+"%s,%s,%d,%.1f-%.1f,%" PRIdMAX ",%" PRIdMAX "\n";
+
+const char reportCSV_bw_read_enhanced_format[] =
+"%s,%s,%d,%.1f-%.1f,%" PRIdMAX ",%" PRIdMAX ",%" PRIdMAX ",%d,%d,%d,%d,%d,%d,%d,%d\n";
+
+const char reportCSV_bw_write_enhanced_format[] =
+"%s,%s,%d,%.1f-%.1f,%" PRIdMAX ",%" PRIdMAX ",%" PRIdMAX ",%" PRIdMAX ",%" PRIdMAX ",%d,%u,%u\n";
+
+const char reportCSV_bw_jitter_loss_format[] =
+"%s,%s,%d,%.1f-%.1f,%" PRIdMAX ",%" PRIdMAX ",%.3f,%" PRIdMAX ",%" PRIdMAX ",%.3f,%" PRIdMAX "\n";
+
+const char reportCSV_bw_jitter_loss_pps_format[] =
+"%s,%s,%d,%.1f-%.1f,%" PRIdMAX ",%" PRIdMAX ",%.3f,%" PRIdMAX ",%" PRIdMAX ",%.3f,%" PRIdMAX ",%" PRIdMAX ",%" PRIdMAX ",%f\n";
+
+/* -------------------------------------------------------------------
+ * warnings
+ * ------------------------------------------------------------------- */
+
+const char warn_window_requested[] =
+" (WARNING: requested %s)";
+
+const char warn_window_small[] = "\
+WARNING: TCP window size set to %d bytes. A small window size\n\
+will give poor performance. See the Iperf documentation.\n";
+
+const char warn_delay_large[] =
+"WARNING: delay too large, reducing from %.1f to 1.0 seconds.\n";
+
+const char warn_no_pathmtu[] =
+"WARNING: Path MTU Discovery may not be enabled.\n";
+
+const char warn_no_ack[]=
+"[%3d] WARNING: did not receive ack of last datagram after %d tries.\n";
+
+const char warn_ack_failed[]=
+"[%3d] WARNING: ack of last datagram failed.\n";
+
+const char warn_fileopen_failed[]=
+"WARNING: Unable to open file stream for transfer\n\
+Using default data stream. \n";
+
+const char unable_to_change_win[]=
+"WARNING: Unable to change the window size\n";
+
+const char opt_estimate[]=
+"Optimal Estimate\n";
+
+#ifdef HAVE_FASTSAMPLING
+const char report_interval_small[] =
+"WARNING: interval too small, increasing to %3.4f milliseconds.\n";
+#else
+const char report_interval_small[] =
+"WARNING: interval too small, increasing to %3.3f milliseconds.\n";
+#endif
+const char warn_invalid_server_option[] =
+"WARNING: option -%c is not valid for server mode\n";
+
+const char warn_invalid_client_option[] =
+"WARNING: option -%c is not valid for client mode\n";
+
+const char warn_invalid_compatibility_option[] =
+"WARNING: option -%c is not valid in compatibility mode\n";
+
+const char warn_implied_udp[] =
+"WARNING: option -%c implies udp testing\n";
+
+const char warn_implied_compatibility[] =
+"WARNING: option -%c has implied compatibility mode\n";
+
+const char warn_buffer_too_small[] =
+"WARNING: %s socket buffer size (-l value) increased to %d bytes for proper operation\n";
+
+const char warn_invalid_single_threaded[] =
+"WARNING: option -%c is not valid in single threaded versions\n";
+
+const char warn_invalid_report_style[] =
+"WARNING: unknown reporting style \"%s\", switching to default\n";
+
+const char warn_invalid_report[] =
+"WARNING: unknown reporting type \"%c\", ignored\n valid options are:\n\t exclude: C(connection) D(data) M(multicast) S(settings) V(server) report\n\n";
+
+const char warn_server_old[] =
+"WARNING: iperf server version is likely very old\n";
+
+const char warn_test_exchange_failed[] =
+"WARNING: client/server version exchange failed\n";
+
+const char warn_len_too_small_peer_exchange[] =
+"WARNING: %s option -l value of %d is too small for peer exchange (suggested min value is %d bytes)\n";
+
+const char warn_compat_and_peer_exchange[] =
+"WARNING: Options of '-C' '--compatibility' AND '-X' '--peerdetect' are mutually exclusive, --peerdetect ignored\n";
+
+const char warn_start_before_now[] =
+"[%3d] WARNING: --txstart-time (%" PRIdMAX ".%" PRIdMAX ") %s is before now %s\n";
+
+const char error_starttime_exceeds[] =
+"ERROR: --txstart-time (%" PRIdMAX ".%" PRIdMAX ") %s exceeds max scheduled delay of %d secs\n";
+
+const char error_delaytime_exceeds[] =
+"ERROR: --txdelay-time of %d seconds is more than the supported delay of %d seconds\n";
+
+#ifdef __cplusplus
+} /* end extern "C" */
+#endif
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..37db611
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,71 @@
+bin_PROGRAMS = iperf
+
+LIBCOMPAT_LDADDS = @STRIP_BEGIN@ \
+ $(top_builddir)/compat/libcompat.a \
+ @STRIP_END@
+
+AM_CPPFLAGS = @STRIP_BEGIN@ \
+ -I$(top_srcdir)/include \
+ -I$(top_builddir)/include \
+ @STRIP_END@
+
+AM_CXXFLAGS = -Wall
+AM_CFLAGS = -Wall
+
+if DEBUG_SYMBOLS
+AM_CFLAGS += -g3 -O0
+AM_CXXFLAGS += -g3 -O0
+else
+AM_CFLAGS += -O2
+AM_CXXFLAGS += -O2
+endif
+
+
+iperf_LDFLAGS = @CFLAGS@ @PTHREAD_CFLAGS@ @WEB100_CFLAGS@ @DEFS@
+
+iperf_SOURCES = \
+ Client.cpp \
+ Extractor.c \
+ isochronous.cpp \
+ Launch.cpp \
+ active_hosts.cpp \
+ Listener.cpp \
+ Locale.c \
+ PerfSocket.cpp \
+ Reporter.c \
+ Reports.c \
+ ReportOutputs.c \
+ Server.cpp \
+ Settings.cpp \
+ SocketAddr.c \
+ gnu_getopt.c \
+ gnu_getopt_long.c \
+ histogram.c \
+ main.cpp \
+ service.c \
+ socket_io.c \
+ stdio.c \
+ packet_ring.c \
+ tcp_window_size.c \
+ pdfs.c \
+ dscp.c \
+ iperf_formattime.c
+iperf_LDADD = $(LIBCOMPAT_LDADDS)
+
+
+if CHECKPROGRAMS
+noinst_PROGRAMS = checkdelay checkpdfs checkisoch igmp_querier
+checkdelay_SOURCES = checkdelay.c
+checkdelay_LDADD = $(LIBCOMPAT_LDADDS)
+checkpdfs_SOURCES = pdfs.c checkpdfs.c stdio.c
+checkpdfs_LDADD = -lm
+checkisoch_SOURCES = checkisoch.cpp isochronous.cpp pdfs.c stdio.c
+igmp_querier_SOURCES = igmp_querier.c
+checkisoch_LDADD = $(LIBCOMPAT_LDADDS)
+endif
+
+
+if AF_PACKET
+iperf_SOURCES += checksums.c
+endif
+
diff --git a/src/Makefile.in b/src/Makefile.in
new file mode 100644
index 0000000..a8ab595
--- /dev/null
+++ b/src/Makefile.in
@@ -0,0 +1,829 @@
+# Makefile.in generated by automake 1.16.2 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+bin_PROGRAMS = iperf$(EXEEXT)
+@DEBUG_SYMBOLS_TRUE@am__append_1 = -g3 -O0
+@DEBUG_SYMBOLS_TRUE@am__append_2 = -g3 -O0
+@DEBUG_SYMBOLS_FALSE@am__append_3 = -O2
+@DEBUG_SYMBOLS_FALSE@am__append_4 = -O2
+@CHECKPROGRAMS_TRUE@noinst_PROGRAMS = checkdelay$(EXEEXT) \
+@CHECKPROGRAMS_TRUE@ checkpdfs$(EXEEXT) checkisoch$(EXEEXT) \
+@CHECKPROGRAMS_TRUE@ igmp_querier$(EXEEXT)
+@AF_PACKET_TRUE@am__append_5 = checksums.c
+subdir = src
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/ax_create_stdint_h.m4 \
+ $(top_srcdir)/m4/dast.m4 $(top_srcdir)/m4/ax_pthread.m4 \
+ $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__installdirs = "$(DESTDIR)$(bindir)"
+PROGRAMS = $(bin_PROGRAMS) $(noinst_PROGRAMS)
+am__checkdelay_SOURCES_DIST = checkdelay.c
+@CHECKPROGRAMS_TRUE@am_checkdelay_OBJECTS = checkdelay.$(OBJEXT)
+checkdelay_OBJECTS = $(am_checkdelay_OBJECTS)
+am__DEPENDENCIES_1 = $(top_builddir)/compat/libcompat.a
+@CHECKPROGRAMS_TRUE@checkdelay_DEPENDENCIES = $(am__DEPENDENCIES_1)
+am__checkisoch_SOURCES_DIST = checkisoch.cpp isochronous.cpp pdfs.c \
+ stdio.c
+@CHECKPROGRAMS_TRUE@am_checkisoch_OBJECTS = checkisoch.$(OBJEXT) \
+@CHECKPROGRAMS_TRUE@ isochronous.$(OBJEXT) pdfs.$(OBJEXT) \
+@CHECKPROGRAMS_TRUE@ stdio.$(OBJEXT)
+checkisoch_OBJECTS = $(am_checkisoch_OBJECTS)
+@CHECKPROGRAMS_TRUE@checkisoch_DEPENDENCIES = $(am__DEPENDENCIES_1)
+am__checkpdfs_SOURCES_DIST = pdfs.c checkpdfs.c stdio.c
+@CHECKPROGRAMS_TRUE@am_checkpdfs_OBJECTS = pdfs.$(OBJEXT) \
+@CHECKPROGRAMS_TRUE@ checkpdfs.$(OBJEXT) stdio.$(OBJEXT)
+checkpdfs_OBJECTS = $(am_checkpdfs_OBJECTS)
+checkpdfs_DEPENDENCIES =
+am__igmp_querier_SOURCES_DIST = igmp_querier.c
+@CHECKPROGRAMS_TRUE@am_igmp_querier_OBJECTS = igmp_querier.$(OBJEXT)
+igmp_querier_OBJECTS = $(am_igmp_querier_OBJECTS)
+igmp_querier_LDADD = $(LDADD)
+am__iperf_SOURCES_DIST = Client.cpp Extractor.c isochronous.cpp \
+ Launch.cpp active_hosts.cpp Listener.cpp Locale.c \
+ PerfSocket.cpp Reporter.c Reports.c ReportOutputs.c Server.cpp \
+ Settings.cpp SocketAddr.c gnu_getopt.c gnu_getopt_long.c \
+ histogram.c main.cpp service.c socket_io.c stdio.c \
+ packet_ring.c tcp_window_size.c pdfs.c dscp.c \
+ iperf_formattime.c checksums.c
+@AF_PACKET_TRUE@am__objects_1 = checksums.$(OBJEXT)
+am_iperf_OBJECTS = Client.$(OBJEXT) Extractor.$(OBJEXT) \
+ isochronous.$(OBJEXT) Launch.$(OBJEXT) active_hosts.$(OBJEXT) \
+ Listener.$(OBJEXT) Locale.$(OBJEXT) PerfSocket.$(OBJEXT) \
+ Reporter.$(OBJEXT) Reports.$(OBJEXT) ReportOutputs.$(OBJEXT) \
+ Server.$(OBJEXT) Settings.$(OBJEXT) SocketAddr.$(OBJEXT) \
+ gnu_getopt.$(OBJEXT) gnu_getopt_long.$(OBJEXT) \
+ histogram.$(OBJEXT) main.$(OBJEXT) service.$(OBJEXT) \
+ socket_io.$(OBJEXT) stdio.$(OBJEXT) packet_ring.$(OBJEXT) \
+ tcp_window_size.$(OBJEXT) pdfs.$(OBJEXT) dscp.$(OBJEXT) \
+ iperf_formattime.$(OBJEXT) $(am__objects_1)
+iperf_OBJECTS = $(am_iperf_OBJECTS)
+iperf_DEPENDENCIES = $(am__DEPENDENCIES_1)
+iperf_LINK = $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(iperf_LDFLAGS) \
+ $(LDFLAGS) -o $@
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/Client.Po ./$(DEPDIR)/Extractor.Po \
+ ./$(DEPDIR)/Launch.Po ./$(DEPDIR)/Listener.Po \
+ ./$(DEPDIR)/Locale.Po ./$(DEPDIR)/PerfSocket.Po \
+ ./$(DEPDIR)/ReportOutputs.Po ./$(DEPDIR)/Reporter.Po \
+ ./$(DEPDIR)/Reports.Po ./$(DEPDIR)/Server.Po \
+ ./$(DEPDIR)/Settings.Po ./$(DEPDIR)/SocketAddr.Po \
+ ./$(DEPDIR)/active_hosts.Po ./$(DEPDIR)/checkdelay.Po \
+ ./$(DEPDIR)/checkisoch.Po ./$(DEPDIR)/checkpdfs.Po \
+ ./$(DEPDIR)/checksums.Po ./$(DEPDIR)/dscp.Po \
+ ./$(DEPDIR)/gnu_getopt.Po ./$(DEPDIR)/gnu_getopt_long.Po \
+ ./$(DEPDIR)/histogram.Po ./$(DEPDIR)/igmp_querier.Po \
+ ./$(DEPDIR)/iperf_formattime.Po ./$(DEPDIR)/isochronous.Po \
+ ./$(DEPDIR)/main.Po ./$(DEPDIR)/packet_ring.Po \
+ ./$(DEPDIR)/pdfs.Po ./$(DEPDIR)/service.Po \
+ ./$(DEPDIR)/socket_io.Po ./$(DEPDIR)/stdio.Po \
+ ./$(DEPDIR)/tcp_window_size.Po
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)
+AM_V_CXX = $(am__v_CXX_@AM_V@)
+am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@)
+am__v_CXX_0 = @echo " CXX " $@;
+am__v_CXX_1 =
+CXXLD = $(CXX)
+CXXLINK = $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) \
+ -o $@
+AM_V_CXXLD = $(am__v_CXXLD_@AM_V@)
+am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@)
+am__v_CXXLD_0 = @echo " CXXLD " $@;
+am__v_CXXLD_1 =
+SOURCES = $(checkdelay_SOURCES) $(checkisoch_SOURCES) \
+ $(checkpdfs_SOURCES) $(igmp_querier_SOURCES) $(iperf_SOURCES)
+DIST_SOURCES = $(am__checkdelay_SOURCES_DIST) \
+ $(am__checkisoch_SOURCES_DIST) $(am__checkpdfs_SOURCES_DIST) \
+ $(am__igmp_querier_SOURCES_DIST) $(am__iperf_SOURCES_DIST)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CXX = @CXX@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+OBJEXT = @OBJEXT@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PTHREAD_CC = @PTHREAD_CC@
+PTHREAD_CFLAGS = @PTHREAD_CFLAGS@
+PTHREAD_LIBS = @PTHREAD_LIBS@
+RANLIB = @RANLIB@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+STRIP_BEGIN = @STRIP_BEGIN@
+STRIP_DUMMY = @STRIP_DUMMY@
+STRIP_END = @STRIP_END@
+VERSION = @VERSION@
+WEB100_CFLAGS = @WEB100_CFLAGS@
+WEB100_CONFIG = @WEB100_CONFIG@
+WEB100_LIBS = @WEB100_LIBS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+ax_pthread_config = @ax_pthread_config@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+LIBCOMPAT_LDADDS = @STRIP_BEGIN@ \
+ $(top_builddir)/compat/libcompat.a \
+ @STRIP_END@
+
+AM_CPPFLAGS = @STRIP_BEGIN@ \
+ -I$(top_srcdir)/include \
+ -I$(top_builddir)/include \
+ @STRIP_END@
+
+AM_CXXFLAGS = -Wall $(am__append_2) $(am__append_4)
+AM_CFLAGS = -Wall $(am__append_1) $(am__append_3)
+iperf_LDFLAGS = @CFLAGS@ @PTHREAD_CFLAGS@ @WEB100_CFLAGS@ @DEFS@
+iperf_SOURCES = Client.cpp Extractor.c isochronous.cpp Launch.cpp \
+ active_hosts.cpp Listener.cpp Locale.c PerfSocket.cpp \
+ Reporter.c Reports.c ReportOutputs.c Server.cpp Settings.cpp \
+ SocketAddr.c gnu_getopt.c gnu_getopt_long.c histogram.c \
+ main.cpp service.c socket_io.c stdio.c packet_ring.c \
+ tcp_window_size.c pdfs.c dscp.c iperf_formattime.c \
+ $(am__append_5)
+iperf_LDADD = $(LIBCOMPAT_LDADDS)
+@CHECKPROGRAMS_TRUE@checkdelay_SOURCES = checkdelay.c
+@CHECKPROGRAMS_TRUE@checkdelay_LDADD = $(LIBCOMPAT_LDADDS)
+@CHECKPROGRAMS_TRUE@checkpdfs_SOURCES = pdfs.c checkpdfs.c stdio.c
+@CHECKPROGRAMS_TRUE@checkpdfs_LDADD = -lm
+@CHECKPROGRAMS_TRUE@checkisoch_SOURCES = checkisoch.cpp isochronous.cpp pdfs.c stdio.c
+@CHECKPROGRAMS_TRUE@igmp_querier_SOURCES = igmp_querier.c
+@CHECKPROGRAMS_TRUE@checkisoch_LDADD = $(LIBCOMPAT_LDADDS)
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .cpp .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu src/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+install-binPROGRAMS: $(bin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-binPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(bindir)" && rm -f $$files
+
+clean-binPROGRAMS:
+ -test -z "$(bin_PROGRAMS)" || rm -f $(bin_PROGRAMS)
+
+clean-noinstPROGRAMS:
+ -test -z "$(noinst_PROGRAMS)" || rm -f $(noinst_PROGRAMS)
+
+checkdelay$(EXEEXT): $(checkdelay_OBJECTS) $(checkdelay_DEPENDENCIES) $(EXTRA_checkdelay_DEPENDENCIES)
+ @rm -f checkdelay$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(checkdelay_OBJECTS) $(checkdelay_LDADD) $(LIBS)
+
+checkisoch$(EXEEXT): $(checkisoch_OBJECTS) $(checkisoch_DEPENDENCIES) $(EXTRA_checkisoch_DEPENDENCIES)
+ @rm -f checkisoch$(EXEEXT)
+ $(AM_V_CXXLD)$(CXXLINK) $(checkisoch_OBJECTS) $(checkisoch_LDADD) $(LIBS)
+
+checkpdfs$(EXEEXT): $(checkpdfs_OBJECTS) $(checkpdfs_DEPENDENCIES) $(EXTRA_checkpdfs_DEPENDENCIES)
+ @rm -f checkpdfs$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(checkpdfs_OBJECTS) $(checkpdfs_LDADD) $(LIBS)
+
+igmp_querier$(EXEEXT): $(igmp_querier_OBJECTS) $(igmp_querier_DEPENDENCIES) $(EXTRA_igmp_querier_DEPENDENCIES)
+ @rm -f igmp_querier$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(igmp_querier_OBJECTS) $(igmp_querier_LDADD) $(LIBS)
+
+iperf$(EXEEXT): $(iperf_OBJECTS) $(iperf_DEPENDENCIES) $(EXTRA_iperf_DEPENDENCIES)
+ @rm -f iperf$(EXEEXT)
+ $(AM_V_CXXLD)$(iperf_LINK) $(iperf_OBJECTS) $(iperf_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Client.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Extractor.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Launch.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Listener.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Locale.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/PerfSocket.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ReportOutputs.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Reporter.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Reports.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Server.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Settings.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/SocketAddr.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/active_hosts.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/checkdelay.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/checkisoch.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/checkpdfs.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/checksums.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dscp.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gnu_getopt.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gnu_getopt_long.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/histogram.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/igmp_querier.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/iperf_formattime.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/isochronous.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/main.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/packet_ring.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pdfs.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/service.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/socket_io.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stdio.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tcp_window_size.Po@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.cpp.o:
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $<
+
+.cpp.obj:
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(PROGRAMS)
+installdirs:
+ for dir in "$(DESTDIR)$(bindir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-binPROGRAMS clean-generic clean-noinstPROGRAMS \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/Client.Po
+ -rm -f ./$(DEPDIR)/Extractor.Po
+ -rm -f ./$(DEPDIR)/Launch.Po
+ -rm -f ./$(DEPDIR)/Listener.Po
+ -rm -f ./$(DEPDIR)/Locale.Po
+ -rm -f ./$(DEPDIR)/PerfSocket.Po
+ -rm -f ./$(DEPDIR)/ReportOutputs.Po
+ -rm -f ./$(DEPDIR)/Reporter.Po
+ -rm -f ./$(DEPDIR)/Reports.Po
+ -rm -f ./$(DEPDIR)/Server.Po
+ -rm -f ./$(DEPDIR)/Settings.Po
+ -rm -f ./$(DEPDIR)/SocketAddr.Po
+ -rm -f ./$(DEPDIR)/active_hosts.Po
+ -rm -f ./$(DEPDIR)/checkdelay.Po
+ -rm -f ./$(DEPDIR)/checkisoch.Po
+ -rm -f ./$(DEPDIR)/checkpdfs.Po
+ -rm -f ./$(DEPDIR)/checksums.Po
+ -rm -f ./$(DEPDIR)/dscp.Po
+ -rm -f ./$(DEPDIR)/gnu_getopt.Po
+ -rm -f ./$(DEPDIR)/gnu_getopt_long.Po
+ -rm -f ./$(DEPDIR)/histogram.Po
+ -rm -f ./$(DEPDIR)/igmp_querier.Po
+ -rm -f ./$(DEPDIR)/iperf_formattime.Po
+ -rm -f ./$(DEPDIR)/isochronous.Po
+ -rm -f ./$(DEPDIR)/main.Po
+ -rm -f ./$(DEPDIR)/packet_ring.Po
+ -rm -f ./$(DEPDIR)/pdfs.Po
+ -rm -f ./$(DEPDIR)/service.Po
+ -rm -f ./$(DEPDIR)/socket_io.Po
+ -rm -f ./$(DEPDIR)/stdio.Po
+ -rm -f ./$(DEPDIR)/tcp_window_size.Po
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-binPROGRAMS
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/Client.Po
+ -rm -f ./$(DEPDIR)/Extractor.Po
+ -rm -f ./$(DEPDIR)/Launch.Po
+ -rm -f ./$(DEPDIR)/Listener.Po
+ -rm -f ./$(DEPDIR)/Locale.Po
+ -rm -f ./$(DEPDIR)/PerfSocket.Po
+ -rm -f ./$(DEPDIR)/ReportOutputs.Po
+ -rm -f ./$(DEPDIR)/Reporter.Po
+ -rm -f ./$(DEPDIR)/Reports.Po
+ -rm -f ./$(DEPDIR)/Server.Po
+ -rm -f ./$(DEPDIR)/Settings.Po
+ -rm -f ./$(DEPDIR)/SocketAddr.Po
+ -rm -f ./$(DEPDIR)/active_hosts.Po
+ -rm -f ./$(DEPDIR)/checkdelay.Po
+ -rm -f ./$(DEPDIR)/checkisoch.Po
+ -rm -f ./$(DEPDIR)/checkpdfs.Po
+ -rm -f ./$(DEPDIR)/checksums.Po
+ -rm -f ./$(DEPDIR)/dscp.Po
+ -rm -f ./$(DEPDIR)/gnu_getopt.Po
+ -rm -f ./$(DEPDIR)/gnu_getopt_long.Po
+ -rm -f ./$(DEPDIR)/histogram.Po
+ -rm -f ./$(DEPDIR)/igmp_querier.Po
+ -rm -f ./$(DEPDIR)/iperf_formattime.Po
+ -rm -f ./$(DEPDIR)/isochronous.Po
+ -rm -f ./$(DEPDIR)/main.Po
+ -rm -f ./$(DEPDIR)/packet_ring.Po
+ -rm -f ./$(DEPDIR)/pdfs.Po
+ -rm -f ./$(DEPDIR)/service.Po
+ -rm -f ./$(DEPDIR)/socket_io.Po
+ -rm -f ./$(DEPDIR)/stdio.Po
+ -rm -f ./$(DEPDIR)/tcp_window_size.Po
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-binPROGRAMS
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-binPROGRAMS clean-generic clean-noinstPROGRAMS \
+ cscopelist-am ctags ctags-am distclean distclean-compile \
+ distclean-generic distclean-tags distdir dvi dvi-am html \
+ html-am info info-am install install-am install-binPROGRAMS \
+ install-data install-data-am install-dvi install-dvi-am \
+ install-exec install-exec-am install-html install-html-am \
+ install-info install-info-am install-man install-pdf \
+ install-pdf-am install-ps install-ps-am install-strip \
+ installcheck installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic pdf pdf-am ps ps-am tags tags-am uninstall \
+ uninstall-am uninstall-binPROGRAMS
+
+.PRECIOUS: Makefile
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/src/PerfSocket.cpp b/src/PerfSocket.cpp
new file mode 100644
index 0000000..bad4074
--- /dev/null
+++ b/src/PerfSocket.cpp
@@ -0,0 +1,445 @@
+/*---------------------------------------------------------------
+ * Copyright (c) 1999,2000,2001,2002,2003
+ * The Board of Trustees of the University of Illinois
+ * All Rights Reserved.
+ *---------------------------------------------------------------
+ * 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.
+ * ________________________________________________________________
+ * National Laboratory for Applied Network Research
+ * National Center for Supercomputing Applications
+ * University of Illinois at Urbana-Champaign
+ * http://www.ncsa.uiuc.edu
+ * ________________________________________________________________
+ *
+ * PerfSocket.cpp
+ * by Mark Gates <mgates@nlanr.net>
+ * Ajay Tirumala <tirumala@ncsa.uiuc.edu>
+ * -------------------------------------------------------------------
+ * Has routines the Client and Server classes use in common for
+ * performance testing the network.
+ * Changes in version 1.2.0
+ * for extracting data from files
+ * -------------------------------------------------------------------
+ * headers
+ * uses
+ * <stdlib.h>
+ * <stdio.h>
+ * <string.h>
+ *
+ * <sys/types.h>
+ * <sys/socket.h>
+ * <unistd.h>
+ *
+ * <arpa/inet.h>
+ * <netdb.h>
+ * <netinet/in.h>
+ * <sys/socket.h>
+ * ------------------------------------------------------------------- */
+#define HEADERS()
+
+#include "headers.h"
+#include "PerfSocket.hpp"
+#include "SocketAddr.h"
+#include "util.h"
+
+/* -------------------------------------------------------------------
+ * Set socket options before the listen() or connect() calls.
+ * These are optional performance tuning factors.
+ * ------------------------------------------------------------------- */
+void SetSocketOptions (struct thread_Settings *inSettings) {
+ // set the TCP window size (socket buffer sizes)
+ // also the UDP buffer size
+ // must occur before call to accept() for large window sizes
+ setsock_tcp_windowsize(inSettings->mSock, inSettings->mTCPWin,
+ (inSettings->mThreadMode == kMode_Client ? 1 : 0));
+
+ if (isCongestionControl(inSettings)) {
+#ifdef TCP_CONGESTION
+ Socklen_t len = strlen(inSettings->mCongestion) + 1;
+ int rc = setsockopt(inSettings->mSock, IPPROTO_TCP, TCP_CONGESTION,
+ inSettings->mCongestion, len);
+ if (rc == SOCKET_ERROR) {
+ fprintf(stderr, "Attempt to set '%s' congestion control failed: %s\n",
+ inSettings->mCongestion, strerror(errno));
+ unsetCongestionControl(inSettings);
+ }
+#else
+ fprintf(stderr, "The -Z option is not available on this operating system\n");
+#endif
+ }
+
+
+#if ((HAVE_TUNTAP_TAP) && (HAVE_TUNTAP_TUN))
+ if (isTunDev(inSettings) || isTapDev(inSettings)) {
+ char **device = (inSettings->mThreadMode == kMode_Client) ? &inSettings->mIfrnametx : &inSettings->mIfrname;
+ struct ifreq ifr;
+ struct sockaddr_ll saddr;
+ memset(&ifr, 0, sizeof(ifr));
+ if (*device) {
+ snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", *device);
+// ifr.ifr_flags = IFF_MULTI_QUEUE;
+ }
+ inSettings->tuntapdev = open("/dev/net/tun", O_RDWR);
+ FAIL_errno((inSettings->tuntapdev == -1), "open tun dev", inSettings);
+ ifr.ifr_flags |= isTapDev(inSettings) ? IFF_TAP : IFF_TUN;
+ ifr.ifr_flags |= IFF_NO_PI;
+ int rc = ioctl(inSettings->tuntapdev, TUNSETIFF, (void*) &ifr);
+ FAIL_errno((rc == -1), "tunsetiff", inSettings);
+ if (!(*device)) {
+ int len = snprintf(NULL, 0, "tap%d", inSettings->tuntapdev);
+ len++; // Trailing null byte + extra
+ (*device) = static_cast<char *>(calloc(0,len));
+ len = snprintf(*device, len, "tap%d", inSettings->tuntapdev);
+ }
+ memset(&saddr, 0, sizeof(saddr));
+ saddr.sll_family = AF_PACKET;
+ saddr.sll_protocol = htons(ETH_P_ALL);
+ saddr.sll_ifindex = if_nametoindex(*device);
+ if (!saddr.sll_ifindex) {
+ fprintf(stderr, "tuntap device of %s used for index lookup\n", (*device));
+ FAIL_errno(!saddr.sll_ifindex, "tuntap nametoindex", inSettings);
+ }
+ saddr.sll_pkttype = PACKET_HOST;
+ rc = bind(inSettings->mSock, reinterpret_cast<sockaddr*>(&saddr), sizeof(saddr));
+ FAIL_errno((rc == SOCKET_ERROR), "tap bind", inSettings);
+#ifdef HAVE_THREAD_DEBUG
+ thread_debug("tuntap device of %s configured", inSettings->mIfrname);
+#endif
+ } else
+#endif
+#if (HAVE_DECL_SO_BINDTODEVICE)
+ {
+ char **device = (inSettings->mThreadMode == kMode_Client) ? &inSettings->mIfrnametx : &inSettings->mIfrname;
+ if (*device) {
+ struct ifreq ifr;
+ memset(&ifr, 0, sizeof(ifr));
+ snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", *device);
+ if (setsockopt(inSettings->mSock, SOL_SOCKET, SO_BINDTODEVICE, (void *)&ifr, sizeof(ifr)) < 0) {
+ char *buf;
+ int len = snprintf(NULL, 0, "%s %s", "bind to device", *device);
+ len++; // Trailing null byte + extra
+ buf = static_cast<char *>(malloc(len));
+ len = snprintf(buf, len, "%s %s", "bind to device", *device);
+ WARN_errno(1, buf);
+ free(buf);
+ free(*device);
+ *device = NULL;
+ FAIL(1, "setsockopt() SO_BINDTODEVICE", inSettings);
+ }
+ }
+ }
+#endif
+
+ // check if we're sending multicast
+ if (isMulticast(inSettings)) {
+#ifdef HAVE_MULTICAST
+ if (!isUDP(inSettings)) {
+ FAIL(1, "Multicast requires -u option ", inSettings);
+ exit(1);
+ }
+ // check for default TTL, multicast is 1 and unicast is the system default
+ if (inSettings->mTTL == -1) {
+ inSettings->mTTL = 1;
+ }
+ if (inSettings->mTTL > 0) {
+ // set TTL
+ if (!isIPV6(inSettings)) {
+ unsigned char cval = inSettings->mTTL;
+ int rc = setsockopt(inSettings->mSock, IPPROTO_IP, IP_MULTICAST_TTL, \
+ reinterpret_cast<const char *>(&cval), sizeof(cval));
+ WARN_errno(rc == SOCKET_ERROR, "multicast v4 ttl");
+ } else
+# ifdef HAVE_IPV6_MULTICAST
+ {
+ int val = inSettings->mTTL;
+ int rc = setsockopt(inSettings->mSock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, \
+ reinterpret_cast<char *>(&val), static_cast<Socklen_t>(sizeof(val)));
+ WARN_errno(rc == SOCKET_ERROR, "multicast v6 ttl");
+ }
+# else
+ FAIL_errno(1, "v6 multicast not supported", inSettings);
+# endif
+ }
+#endif
+ } else if (inSettings->mTTL > 0) {
+ int val = inSettings->mTTL;
+ int rc = setsockopt(inSettings->mSock, IPPROTO_IP, IP_TTL, \
+ reinterpret_cast<char *>(&val), static_cast<Socklen_t>(sizeof(val)));
+ WARN_errno(rc == SOCKET_ERROR, "v4 ttl");
+ }
+
+#if HAVE_DECL_IP_TOS
+ SetSocketOptionsIPTos(inSettings, inSettings->mTOS);
+#endif
+
+ if (!isUDP(inSettings)) {
+ if (isTCPMSS(inSettings)) {
+ // set the TCP maximum segment size
+ setsock_tcp_mss(inSettings->mSock, inSettings->mMSS);
+ }
+#if HAVE_DECL_TCP_NODELAY
+ {
+ int nodelay = 1;
+ Socklen_t len = sizeof(nodelay);
+ int rc = 0;
+ // set TCP nodelay option
+ if (isNoDelay(inSettings)) {
+ rc = setsockopt(inSettings->mSock, IPPROTO_TCP, TCP_NODELAY,
+ reinterpret_cast<char*>(&nodelay), len);
+ WARN_errno(rc == SOCKET_ERROR, "setsockopt TCP_NODELAY");
+ }
+ // Read the socket setting, could be set on by kernel
+ if (isEnhanced(inSettings) && (rc == 0)) {
+ rc = getsockopt(inSettings->mSock, IPPROTO_TCP, TCP_NODELAY,
+ reinterpret_cast<char*>(&nodelay), &len);
+ WARN_errno(rc == SOCKET_ERROR, "getsockopt TCP_NODELAY");
+ if (rc == 0) {
+ if (nodelay)
+ setNoDelay(inSettings);
+ else
+ unsetNoDelay(inSettings);
+ }
+ }
+ }
+#endif
+#if HAVE_DECL_TCP_WINDOW_CLAMP
+ // set TCP clamp option
+ if (isRxClamp(inSettings)) {
+ int clamp = inSettings->mClampSize;
+ Socklen_t len = sizeof(clamp);
+ int rc = setsockopt(inSettings->mSock, IPPROTO_TCP, TCP_WINDOW_CLAMP,
+ reinterpret_cast<char*>(&clamp), len);
+ WARN_errno(rc == SOCKET_ERROR, "setsockopt TCP_WINDOW_CLAMP");
+ }
+#endif
+#if HAVE_DECL_TCP_NOTSENT_LOWAT
+ // set TCP not sent low watermark
+ if (isWritePrefetch(inSettings)) {
+ int bytecnt = inSettings->mWritePrefetch;
+ Socklen_t len = sizeof(bytecnt);
+ int rc = setsockopt(inSettings->mSock, IPPROTO_TCP, TCP_NOTSENT_LOWAT,
+ reinterpret_cast<char*>(&bytecnt), len);
+ WARN_errno(rc == SOCKET_ERROR, "setsockopt TCP_NOTSENT_LOWAT");
+ }
+#endif
+ }
+
+#if HAVE_DECL_SO_MAX_PACING_RATE
+ /* If socket pacing is specified try to enable it. */
+ if (isFQPacing(inSettings) && inSettings->mFQPacingRate > 0) {
+ int rc = setsockopt(inSettings->mSock, SOL_SOCKET, SO_MAX_PACING_RATE, &inSettings->mFQPacingRate, sizeof(inSettings->mFQPacingRate));
+ WARN_errno(rc == SOCKET_ERROR, "setsockopt SO_MAX_PACING_RATE");
+ }
+#endif /* HAVE_SO_MAX_PACING_RATE */
+#if HAVE_DECL_SO_DONTROUTE
+ /* If socket pacing is specified try to enable it. */
+ if (isDontRoute(inSettings)) {
+ int option = 1;
+ Socklen_t len = sizeof(option);
+ int rc = setsockopt(inSettings->mSock, SOL_SOCKET, SO_DONTROUTE, reinterpret_cast<char*>(&option), len);
+ WARN_errno(rc == SOCKET_ERROR, "setsockopt SO_DONTROUTE");
+ }
+#endif /* HAVE_DECL_SO_DONTROUTE */
+}
+
+// Note that timer units are microseconds, be careful
+void SetSocketOptionsSendTimeout (struct thread_Settings *mSettings, int timer) {
+ assert (timer > 0);
+#ifdef WIN32
+ // Windows SO_SNDTIMEO uses ms
+ DWORD timeout = (double) timer / 1e3;
+#else
+ struct timeval timeout;
+ timeout.tv_sec = timer / 1000000;
+ timeout.tv_usec = timer % 1000000;
+#endif
+ if (setsockopt(mSettings->mSock, SOL_SOCKET, SO_SNDTIMEO, reinterpret_cast<char *>(&timeout), sizeof(timeout)) < 0) {
+ WARN_errno(mSettings->mSock == SO_SNDTIMEO, "socket");
+ }
+// fprintf(stderr,"**** tx timeout %d usecs\n", timer);
+}
+
+void SetSocketOptionsReceiveTimeout (struct thread_Settings *mSettings, int timer) {
+ assert(timer>0);
+#ifdef WIN32
+ // Windows SO_RCVTIMEO uses ms
+ DWORD timeout = (double) timer / 1e3;
+#else
+ struct timeval timeout;
+ timeout.tv_sec = timer / 1000000;
+ timeout.tv_usec = timer % 1000000;
+#endif
+ if (setsockopt(mSettings->mSock, SOL_SOCKET, SO_RCVTIMEO, reinterpret_cast<char *>(&timeout), sizeof(timeout)) < 0) {
+ WARN_errno(mSettings->mSock == SO_RCVTIMEO, "socket");
+ }
+// fprintf(stderr,"**** rx timeout %d usecs\n", timer);
+}
+
+
+void SetSocketOptionsIPTos (struct thread_Settings *mSettings, int tos) {
+#if HAVE_DECL_IP_TOS
+#ifdef HAVE_THREAD_DEBUG
+ thread_debug("Set socket IP_TOS to 0x%x", tos);
+#endif
+#if HAVE_DECL_IPV6_TCLASS && ! defined HAVE_WINSOCK2_H
+ // IPV6_TCLASS is defined on Windows but not implemented.
+ if (isIPV6(mSettings)) {
+ const int dscp = tos;
+ int rc = setsockopt(mSettings->mSock, IPPROTO_IPV6, IPV6_TCLASS, (char*) &dscp, sizeof(dscp));
+ WARN_errno(rc == SOCKET_ERROR, "setsockopt IPV6_TCLASS");
+ } else
+#endif
+ // set IP TOS (type-of-service) field
+ if (isOverrideTOS(mSettings) || (tos > 0)) {
+ int reqtos = tos;
+ Socklen_t len = sizeof(reqtos);
+ int rc = setsockopt(mSettings->mSock, IPPROTO_IP, IP_TOS,
+ reinterpret_cast<char*>(&reqtos), len);
+ WARN_errno(rc == SOCKET_ERROR, "setsockopt IP_TOS");
+ rc = getsockopt(mSettings->mSock, IPPROTO_IP, IP_TOS,
+ reinterpret_cast<char*>(&reqtos), &len);
+ WARN_errno(rc == SOCKET_ERROR, "getsockopt IP_TOS");
+ WARN((reqtos != tos), "IP_TOS setting failed");
+ }
+#endif
+}
+
+
+/*
+ * Set a socket to blocking or non-blocking
+*
+ * Returns true on success, or false if there was an error
+*/
+bool setsock_blocking (int fd, bool blocking) {
+ if (fd < 0) return false;
+#ifdef WIN32
+ unsigned long mode = blocking ? 0 : 1;
+ return (ioctlsocket(fd, FIONBIO, &mode) == 0) ? true : false;
+#else
+ int flags = fcntl(fd, F_GETFL, 0);
+ if (flags < 0) return false;
+ flags = blocking ? (flags&~O_NONBLOCK) : (flags|O_NONBLOCK);
+ return (fcntl(fd, F_SETFL, flags) == 0) ? true : false;
+#endif
+}
+
+/* -------------------------------------------------------------------
+ * If inMSS > 0, set the TCP maximum segment size for inSock.
+ * Otherwise leave it as the system default.
+ * ------------------------------------------------------------------- */
+
+const char warn_mss_fail[] = "\
+WARNING: attempt to set TCP maxmimum segment size to %d failed\n";
+
+void setsock_tcp_mss (int inSock, int inMSS) {
+#if HAVE_DECL_TCP_MAXSEG
+ int rc;
+ int newMSS;
+ Socklen_t len;
+
+ assert(inSock != INVALID_SOCKET);
+
+ if (inMSS > 0) {
+ /* set */
+ newMSS = inMSS;
+ len = sizeof(newMSS);
+ rc = setsockopt(inSock, IPPROTO_TCP, TCP_MAXSEG, (char*) &newMSS, len);
+ if (rc == SOCKET_ERROR) {
+ fprintf(stderr, warn_mss_fail, newMSS);
+ return;
+ }
+ }
+#endif
+} /* end setsock_tcp_mss */
+
+/* -------------------------------------------------------------------
+ * returns the TCP maximum segment size
+ * ------------------------------------------------------------------- */
+
+int getsock_tcp_mss (int inSock) {
+ int theMSS = -1;
+#if HAVE_DECL_TCP_MAXSEG
+ int rc;
+ Socklen_t len;
+ assert(inSock >= 0);
+
+ /* query for MSS */
+ len = sizeof(theMSS);
+ rc = getsockopt(inSock, IPPROTO_TCP, TCP_MAXSEG, (char*)&theMSS, &len);
+ WARN_errno(rc == SOCKET_ERROR, "getsockopt TCP_MAXSEG");
+#endif
+ return theMSS;
+} /* end getsock_tcp_mss */
+
+#ifdef DEFAULT_PAYLOAD_LEN_PER_MTU_DISCOVERY
+#define UDPMAXSIZE ((1024 * 64) - 64) // 16 bit field for UDP
+void checksock_max_udp_payload (struct thread_Settings *inSettings) {
+#if HAVE_DECL_SIOCGIFMTU
+ struct ifreq ifr;
+ if (!isBuflenSet(inSettings) && inSettings->mIfrname) {
+ strncpy(ifr.ifr_name, inSettings->mIfrname, (size_t) (IFNAMSIZ - 1));
+ if (!ioctl(inSettings->mSock, SIOCGIFMTU, &ifr)) {
+ int max;
+ if (!isIPV6(inSettings)) {
+ max = ifr.ifr_mtu - IPV4HDRLEN - UDPHDRLEN;
+ } else {
+ max = ifr.ifr_mtu - IPV6HDRLEN - UDPHDRLEN;
+ }
+ if ((max > 0) && (max != inSettings->mBufLen)) {
+ if (max > UDPMAXSIZE) {
+ max = UDPMAXSIZE;
+ }
+ if (max > inSettings->mBufLen) {
+ char *tmp = new char[max];
+ assert(tmp!=NULL);
+ if (tmp) {
+ pattern(tmp, max);
+ memcpy(tmp, inSettings->mBuf, inSettings->mBufLen);
+ DELETE_ARRAY(inSettings->mBuf);
+ inSettings->mBuf = tmp;
+ inSettings->mBufLen = max;
+ }
+ } else {
+ inSettings->mBufLen = max;
+ }
+ }
+ }
+ }
+#endif
+}
+#endif
+
+// end SetSocketOptions
diff --git a/src/ReportOutputs.c b/src/ReportOutputs.c
new file mode 100644
index 0000000..e977433
--- /dev/null
+++ b/src/ReportOutputs.c
@@ -0,0 +1,2019 @@
+/*---------------------------------------------------------------
+ * Copyright (c) 2020
+ * Broadcom Corporation
+ * All Rights Reserved.
+ *---------------------------------------------------------------
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software 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 name of Broadcom Coporation,
+ * 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.
+ * ________________________________________________________________
+ *
+ * reporter output routines
+ *
+ * by Robert J. McMahon (rjmcmahon@rjmcmahon.com, bob.mcmahon@broadcom.com)
+ * -------------------------------------------------------------------
+ */
+#include <math.h>
+#include "headers.h"
+#include "Settings.hpp"
+#include "Reporter.h"
+#include "Locale.h"
+#include "SocketAddr.h"
+#include "iperf_formattime.h"
+
+// These static variables are not thread safe but ok to use becase only
+// the repoter thread usses them
+#define SNBUFFERSIZE 512
+#define SNBUFFEREXTENDSIZE 512
+static char outbuffer[SNBUFFERSIZE]; // Buffer for printing
+static char outbufferext[SNBUFFEREXTENDSIZE]; // Buffer for printing
+
+#define LLAWBUFSIZE 100
+static char netpower_buf[100];
+
+static int HEADING_FLAG(report_bw) = 0;
+static int HEADING_FLAG(report_client_bb_bw) = 0;
+static int HEADING_FLAG(report_bw_jitter_loss) = 0;
+static int HEADING_FLAG(report_bw_read_enhanced) = 0;
+static int HEADING_FLAG(report_bw_read_enhanced_netpwr) = 0;
+static int HEADING_FLAG(report_bw_write_enhanced) = 0;
+static int HEADING_FLAG(report_write_enhanced_write) = 0;
+static int HEADING_FLAG(report_bw_write_enhanced_netpwr) = 0;
+static int HEADING_FLAG(report_bw_pps_enhanced) = 0;
+static int HEADING_FLAG(report_bw_pps_enhanced_isoch) = 0;
+static int HEADING_FLAG(report_bw_jitter_loss_pps) = 0;
+static int HEADING_FLAG(report_bw_jitter_loss_enhanced) = 0;
+static int HEADING_FLAG(report_bw_jitter_loss_enhanced_isoch) = 0;
+static int HEADING_FLAG(report_write_enhanced_isoch) = 0;
+static int HEADING_FLAG(report_frame_jitter_loss_enhanced) = 0;
+static int HEADING_FLAG(report_frame_tcp_enhanced) = 0;
+static int HEADING_FLAG(report_frame_read_tcp_enhanced_triptime) = 0;
+static int HEADING_FLAG(report_udp_fullduplex) = 0;
+static int HEADING_FLAG(report_sumcnt_bw) = 0;
+static int HEADING_FLAG(report_sumcnt_udp_fullduplex) = 0;
+static int HEADING_FLAG(report_sumcnt_bw_read_enhanced) = 0;
+static int HEADING_FLAG(report_sumcnt_bw_read_triptime) = 0;
+static int HEADING_FLAG(report_sumcnt_bw_write_enhanced) = 0;
+static int HEADING_FLAG(report_sumcnt_bw_pps_enhanced) = 0;
+static int HEADING_FLAG(report_bw_jitter_loss_enhanced_triptime) = 0;
+static int HEADING_FLAG(report_bw_jitter_loss_enhanced_isoch_triptime) = 0;
+static int HEADING_FLAG(report_sumcnt_bw_jitter_loss) = 0;
+static int HEADING_FLAG(report_burst_read_tcp) = 0;
+static int HEADING_FLAG(report_burst_write_tcp) = 0;
+static int HEADING_FLAG(report_bw_isoch_enhanced_netpwr) = 0;
+static int HEADING_FLAG(report_sumcnt_udp_enhanced) = 0;
+static int HEADING_FLAG(report_sumcnt_udp_triptime) = 0;
+
+void reporter_default_heading_flags (int flag) {
+ HEADING_FLAG(report_bw) = flag;
+ HEADING_FLAG(report_client_bb_bw) = flag;
+ HEADING_FLAG(report_sumcnt_bw) = flag;
+ HEADING_FLAG(report_sumcnt_udp_fullduplex) = flag;
+ HEADING_FLAG(report_bw_jitter_loss) = flag;
+ HEADING_FLAG(report_bw_read_enhanced) = flag;
+ HEADING_FLAG(report_bw_read_enhanced_netpwr) = flag;
+ HEADING_FLAG(report_bw_write_enhanced) = flag;
+ HEADING_FLAG(report_write_enhanced_write) = flag;
+ HEADING_FLAG(report_write_enhanced_isoch) = flag;
+ HEADING_FLAG(report_bw_write_enhanced_netpwr) = flag;
+ HEADING_FLAG(report_bw_pps_enhanced) = flag;
+ HEADING_FLAG(report_bw_pps_enhanced_isoch) = flag;
+ HEADING_FLAG(report_bw_jitter_loss_pps) = flag;
+ HEADING_FLAG(report_bw_jitter_loss_enhanced) = flag;
+ HEADING_FLAG(report_bw_jitter_loss_enhanced_isoch) = flag;
+ HEADING_FLAG(report_frame_jitter_loss_enhanced) = flag;
+ HEADING_FLAG(report_frame_tcp_enhanced) = flag;
+ HEADING_FLAG(report_frame_read_tcp_enhanced_triptime) = flag;
+ HEADING_FLAG(report_sumcnt_bw_read_enhanced) = flag;
+ HEADING_FLAG(report_sumcnt_bw_read_triptime) = flag;
+ HEADING_FLAG(report_sumcnt_bw_write_enhanced) = flag;
+ HEADING_FLAG(report_udp_fullduplex) = flag;
+ HEADING_FLAG(report_sumcnt_bw_jitter_loss) = flag;
+ HEADING_FLAG(report_sumcnt_bw_pps_enhanced) = flag;
+ HEADING_FLAG(report_burst_read_tcp) = flag;
+ HEADING_FLAG(report_burst_write_tcp) = flag;
+ HEADING_FLAG(report_bw_isoch_enhanced_netpwr) = flag;
+ HEADING_FLAG(report_sumcnt_udp_enhanced) = flag;
+ HEADING_FLAG(report_sumcnt_udp_triptime) = flag;
+}
+static inline void _print_stats_common (struct TransferInfo *stats) {
+ assert(stats!=NULL);
+ outbuffer[0] = '\0';
+ outbufferext[0] = '\0';
+ byte_snprintf(outbuffer, sizeof(outbuffer), (double) stats->cntBytes, toupper((int)stats->common->Format));
+ if (stats->ts.iEnd < SMALLEST_INTERVAL_SEC) {
+ stats->cntBytes = 0;
+ }
+ byte_snprintf(outbufferext, sizeof(outbufferext), (double)stats->cntBytes / (stats->ts.iEnd - stats->ts.iStart), \
+ stats->common->Format);
+ outbuffer[sizeof(outbuffer)-1]='\0';
+ outbufferext[sizeof(outbufferext)-1]='\0';
+}
+
+static inline void _output_outoforder(struct TransferInfo *stats) {
+ if (stats->cntOutofOrder > 0) {
+ printf(report_outoforder,
+ stats->common->transferIDStr, stats->ts.iStart,
+ stats->ts.iEnd, stats->cntOutofOrder);
+ }
+ if (stats->l2counts.cnt) {
+ printf(report_l2statistics,
+ stats->common->transferIDStr, stats->ts.iStart,
+ stats->ts.iEnd, stats->l2counts.cnt, stats->l2counts.lengtherr,
+ stats->l2counts.udpcsumerr, stats->l2counts.unknown);
+ }
+}
+
+//
+// Little's law is L = lambda * W, where L is queue depth,
+// lambda the arrival rate and W is the processing time
+//
+#define LLAW_LOWERBOUNDS -1e7
+
+static inline void human_format_llawbuf(char *dststr, size_t len, double inP) {
+ if (inP < LLAW_LOWERBOUNDS) {
+ char oobstr[] = "OBL";
+ if (len > sizeof(oobstr))
+ strcpy(dststr, oobstr);
+ } else {
+ //force to adpative bytes for human readable
+ byte_snprintf(dststr, len, inP, 'A');
+ dststr[len-1] = '\0';
+ }
+}
+
+#if 0
+static inline void set_llawbuf_frames (int lambda, double meantransit, double variance, intmax_t framecnt) {
+ int Lvar = 0;
+ int L = round(lambda * meantransit);
+ if (variance > 0.0) {
+ Lvar = round(lambda * variance);
+ } else {
+ Lvar = 0;
+ }
+ snprintf(llaw_buf, sizeof(llaw_buf), "%" PRIdMAX "/%d(%d) frames", framecnt, L, Lvar);
+ llaw_buf[sizeof(llaw_buf) - 1] = '\0';
+}
+#endif
+
+#define NETPWR_LOWERBOUNDS -1e7
+static inline void set_netpowerbuf(double meantransit, struct TransferInfo *stats) {
+ if (meantransit == 0.0) {
+ strcpy(netpower_buf, "NAN");
+ } else {
+ double netpwr = NETPOWERCONSTANT * (((double) stats->cntBytes) / (stats->ts.iEnd - stats->ts.iStart) / meantransit);
+ if (netpwr < NETPWR_LOWERBOUNDS) {
+ strcpy(netpower_buf, "OBL");
+ } else if (netpwr > 100) {
+ snprintf(netpower_buf, sizeof(netpower_buf), "%.0f", netpwr);
+ } else if (netpwr > 10) {
+ snprintf(netpower_buf, sizeof(netpower_buf), "%.2f", netpwr);
+ } else {
+ snprintf(netpower_buf, sizeof(netpower_buf), "%.6f", netpwr);
+ }
+ }
+}
+
+//TCP Output
+void tcp_output_fullduplex (struct TransferInfo *stats) {
+ HEADING_PRINT_COND(report_bw);
+ _print_stats_common(stats);
+ printf(report_bw_sum_fullduplex_format, stats->common->transferIDStr, stats->ts.iStart, stats->ts.iEnd, outbuffer, outbufferext);
+ fflush(stdout);
+}
+
+void tcp_output_fullduplex_sum (struct TransferInfo *stats) {
+ HEADING_PRINT_COND(report_bw);
+ _print_stats_common(stats);
+ printf(report_sum_bw_format, stats->ts.iStart, stats->ts.iEnd, outbuffer, outbufferext);
+ fflush(stdout);
+}
+
+void tcp_output_fullduplex_enhanced (struct TransferInfo *stats) {
+ HEADING_PRINT_COND(report_bw);
+ _print_stats_common(stats);
+ printf(report_bw_sum_fullduplex_enhanced_format, stats->common->transferID, stats->ts.iStart, stats->ts.iEnd, outbuffer, outbufferext);
+ fflush(stdout);
+}
+
+void tcp_output_read (struct TransferInfo *stats) {
+ HEADING_PRINT_COND(report_bw);
+ _print_stats_common(stats);
+ printf(report_bw_format, stats->common->transferIDStr, stats->ts.iStart, stats->ts.iEnd, outbuffer, outbufferext);
+ fflush(stdout);
+}
+//TCP read or server output
+void tcp_output_read_enhanced (struct TransferInfo *stats) {
+ HEADING_PRINT_COND(report_bw_read_enhanced);
+ _print_stats_common(stats);
+ printf(report_bw_read_enhanced_format,
+ stats->common->transferIDStr, stats->ts.iStart, stats->ts.iEnd,
+ outbuffer, outbufferext,
+ stats->sock_callstats.read.cntRead,
+ stats->sock_callstats.read.bins[0],
+ stats->sock_callstats.read.bins[1],
+ stats->sock_callstats.read.bins[2],
+ stats->sock_callstats.read.bins[3],
+ stats->sock_callstats.read.bins[4],
+ stats->sock_callstats.read.bins[5],
+ stats->sock_callstats.read.bins[6],
+ stats->sock_callstats.read.bins[7]);
+ fflush(stdout);
+}
+void tcp_output_read_triptime (struct TransferInfo *stats) {
+ double meantransit;
+ HEADING_PRINT_COND(report_bw_read_enhanced_netpwr);
+ char llaw_bufstr[LLAWBUFSIZE];
+ human_format_llawbuf(llaw_bufstr, sizeof(llaw_bufstr), ((stats->final) ? stats->fInP : stats->iInP));
+ _print_stats_common(stats);
+ if (!stats->final) {
+ meantransit = (stats->transit.current.cnt > 0) ? (stats->transit.current.sum / stats->transit.current.cnt) : 0;
+ set_netpowerbuf(meantransit, stats);
+ printf(report_bw_read_enhanced_netpwr_format,
+ stats->common->transferIDStr, stats->ts.iStart, stats->ts.iEnd,
+ outbuffer, outbufferext,
+ (meantransit * 1e3),
+ (stats->transit.current.cnt < 2) ? 0 : stats->transit.current.min * 1e3,
+ (stats->transit.current.cnt < 2) ? 0 : stats->transit.current.max * 1e3,
+ (stats->transit.current.cnt < 2) ? 0 : 1e3 * (sqrt(stats->transit.current.m2 / (stats->transit.current.cnt - 1))),
+ stats->transit.current.cnt,
+ stats->transit.current.cnt ? (long) ((double)stats->cntBytes / (double) stats->transit.current.cnt) : 0,
+ llaw_bufstr,
+ netpower_buf,
+ stats->sock_callstats.read.cntRead,
+ stats->sock_callstats.read.bins[0],
+ stats->sock_callstats.read.bins[1],
+ stats->sock_callstats.read.bins[2],
+ stats->sock_callstats.read.bins[3],
+ stats->sock_callstats.read.bins[4],
+ stats->sock_callstats.read.bins[5],
+ stats->sock_callstats.read.bins[6],
+ stats->sock_callstats.read.bins[7]);
+ } else {
+ meantransit = (stats->transit.total.cnt > 0) ? (stats->transit.total.sum / stats->transit.total.cnt) : 0;
+ set_netpowerbuf(meantransit, stats);
+ printf(report_bw_read_enhanced_netpwr_format,
+ stats->common->transferIDStr, stats->ts.iStart, stats->ts.iEnd,
+ outbuffer, outbufferext,
+ (meantransit * 1e3),
+ stats->transit.total.min * 1e3,
+ stats->transit.total.max * 1e3,
+ (stats->transit.total.cnt < 2) ? 0 : 1e3 * (sqrt(stats->transit.total.m2 / (stats->transit.total.cnt - 1))),
+ stats->transit.total.cnt,
+ stats->transit.total.cnt ? (long) ((double)stats->cntBytes / (double) stats->transit.total.cnt) : 0,
+ llaw_bufstr,
+ netpower_buf,
+ stats->sock_callstats.read.cntRead,
+ stats->sock_callstats.read.bins[0],
+ stats->sock_callstats.read.bins[1],
+ stats->sock_callstats.read.bins[2],
+ stats->sock_callstats.read.bins[3],
+ stats->sock_callstats.read.bins[4],
+ stats->sock_callstats.read.bins[5],
+ stats->sock_callstats.read.bins[6],
+ stats->sock_callstats.read.bins[7]);
+ }
+ if (stats->framelatency_histogram) {
+ histogram_print(stats->framelatency_histogram, stats->ts.iStart, stats->ts.iEnd);
+ }
+ fflush(stdout);
+}
+void tcp_output_read_enhanced_isoch (struct TransferInfo *stats) {
+ HEADING_PRINT_COND(report_bw_isoch_enhanced_netpwr);
+ _print_stats_common(stats);
+ double meantransit;
+ if (!stats->final) {
+ meantransit = (stats->isochstats.transit.current.cnt > 0) ? (stats->isochstats.transit.current.sum / stats->isochstats.transit.current.cnt) : 0;
+ set_netpowerbuf(meantransit, stats);
+ printf(report_bw_isoch_enhanced_netpwr_format,
+ stats->common->transferIDStr, stats->ts.iStart, stats->ts.iEnd,
+ outbuffer, outbufferext,
+ (meantransit * 1e3),
+ (stats->isochstats.transit.current.cnt < 2) ? 0 : stats->isochstats.transit.current.min * 1e3,
+ (stats->isochstats.transit.current.cnt < 2) ? 0 : stats->isochstats.transit.current.max * 1e3,
+ (stats->isochstats.transit.current.cnt < 2) ? 0 : 1e3 * (sqrt(stats->isochstats.transit.current.m2 / (stats->isochstats.transit.current.cnt - 1))),
+ stats->isochstats.transit.current.cnt,
+ stats->isochstats.transit.current.cnt ? (long) ((double)stats->cntBytes / (double) stats->isochstats.transit.current.cnt) : 0,
+ netpower_buf,
+ stats->sock_callstats.read.cntRead,
+ stats->sock_callstats.read.bins[0],
+ stats->sock_callstats.read.bins[1],
+ stats->sock_callstats.read.bins[2],
+ stats->sock_callstats.read.bins[3],
+ stats->sock_callstats.read.bins[4],
+ stats->sock_callstats.read.bins[5],
+ stats->sock_callstats.read.bins[6],
+ stats->sock_callstats.read.bins[7]);
+ } else {
+ meantransit = (stats->isochstats.transit.total.cnt > 0) ? (stats->isochstats.transit.total.sum / stats->isochstats.transit.total.cnt) : 0;
+ set_netpowerbuf(meantransit, stats);
+ printf(report_bw_isoch_enhanced_netpwr_format,
+ stats->common->transferIDStr, stats->ts.iStart, stats->ts.iEnd,
+ outbuffer, outbufferext,
+ (meantransit * 1e3),
+ stats->isochstats.transit.total.min * 1e3,
+ stats->isochstats.transit.total.max * 1e3,
+ (stats->isochstats.transit.total.cnt < 2) ? 0 : 1e3 * (sqrt(stats->isochstats.transit.total.m2 / (stats->isochstats.transit.total.cnt - 1))),
+ stats->isochstats.transit.total.cnt,
+ stats->isochstats.transit.total.cnt ? (long) ((double)stats->cntBytes / (double) stats->isochstats.transit.total.cnt) : 0,
+ netpower_buf,
+ stats->sock_callstats.read.cntRead,
+ stats->sock_callstats.read.bins[0],
+ stats->sock_callstats.read.bins[1],
+ stats->sock_callstats.read.bins[2],
+ stats->sock_callstats.read.bins[3],
+ stats->sock_callstats.read.bins[4],
+ stats->sock_callstats.read.bins[5],
+ stats->sock_callstats.read.bins[6],
+ stats->sock_callstats.read.bins[7]);
+ }
+ if (stats->framelatency_histogram) {
+ histogram_print(stats->framelatency_histogram, stats->ts.iStart, stats->ts.iEnd);
+ }
+ fflush(stdout);
+}
+
+void tcp_output_frame_read (struct TransferInfo *stats) {
+ HEADING_PRINT_COND(report_frame_tcp_enhanced);
+ _print_stats_common(stats);
+ printf(report_bw_read_enhanced_format,
+ stats->common->transferIDStr, stats->ts.iStart, stats->ts.iEnd,
+ outbuffer, outbufferext,
+ stats->sock_callstats.read.cntRead,
+ stats->sock_callstats.read.bins[0],
+ stats->sock_callstats.read.bins[1],
+ stats->sock_callstats.read.bins[2],
+ stats->sock_callstats.read.bins[3],
+ stats->sock_callstats.read.bins[4],
+ stats->sock_callstats.read.bins[5],
+ stats->sock_callstats.read.bins[6],
+ stats->sock_callstats.read.bins[7]);
+ fflush(stdout);
+}
+void tcp_output_frame_read_triptime (struct TransferInfo *stats) {
+ fprintf(stderr, "FIXME\n");
+}
+void tcp_output_burst_read (struct TransferInfo *stats) {
+ HEADING_PRINT_COND(report_burst_read_tcp);
+ _print_stats_common(stats);
+ if (!stats->final) {
+ set_netpowerbuf(stats->transit.current.mean, stats);
+ printf(report_burst_read_tcp_format,
+ stats->common->transferIDStr, stats->ts.iStart, stats->ts.iEnd,
+ outbuffer, outbufferext,
+ stats->transit.current.mean * 1e3,
+ (1e2 * stats->transit.current.mean * stats->common->FPS), // (1e3 / 100%)
+ stats->sock_callstats.read.cntRead,
+ stats->sock_callstats.read.bins[0],
+ stats->sock_callstats.read.bins[1],
+ stats->sock_callstats.read.bins[2],
+ stats->sock_callstats.read.bins[3],
+ stats->sock_callstats.read.bins[4],
+ stats->sock_callstats.read.bins[5],
+ stats->sock_callstats.read.bins[6],
+ stats->sock_callstats.read.bins[7],
+ netpower_buf);
+ } else {
+ printf(report_burst_read_tcp_final_format,
+ stats->common->transferIDStr, stats->ts.iStart, stats->ts.iEnd,
+ outbuffer, outbufferext,
+ stats->transit.total.mean * 1e3,
+ (stats->transit.total.cnt < 2) ? 0 : stats->transit.total.min * 1e3,
+ (stats->transit.total.cnt < 2) ? 0 : stats->transit.total.max * 1e3,
+ (stats->transit.total.cnt < 2) ? 0 : 1e3 * (sqrt(stats->transit.total.m2 / (stats->transit.total.cnt - 1))),
+ stats->sock_callstats.read.cntRead,
+ stats->sock_callstats.read.bins[0],
+ stats->sock_callstats.read.bins[1],
+ stats->sock_callstats.read.bins[2],
+ stats->sock_callstats.read.bins[3],
+ stats->sock_callstats.read.bins[4],
+ stats->sock_callstats.read.bins[5],
+ stats->sock_callstats.read.bins[6],
+ stats->sock_callstats.read.bins[7]);
+ }
+ fflush(stdout);
+}
+
+//TCP write or client output
+void tcp_output_write (struct TransferInfo *stats) {
+ HEADING_PRINT_COND(report_bw);
+ _print_stats_common(stats);
+ printf(report_bw_format, stats->common->transferIDStr,
+ stats->ts.iStart, stats->ts.iEnd,
+ outbuffer, outbufferext);
+ fflush(stdout);
+}
+
+void tcp_output_write_bb (struct TransferInfo *stats) {
+ HEADING_PRINT_COND(report_client_bb_bw);
+ _print_stats_common(stats);
+ char rps_string[80];
+ if (stats->final) {
+ double rps = ((stats->fBBrunning > 0) && (stats->bbrtt.total.cnt > 0)) ? ((double) stats->bbrtt.total.cnt / stats->fBBrunning) : 0;
+ if (rps < 10)
+ snprintf(rps_string, sizeof(rps_string), "%0.1f", rps);
+ else
+ snprintf(rps_string, sizeof(rps_string), "%0.0f", rps);
+ rps_string[sizeof(rps_string) - 1] = '\0';
+
+#if HAVE_TCP_STATS
+ printf(report_client_bb_bw_format, stats->common->transferIDStr,
+ stats->ts.iStart, stats->ts.iEnd,
+ outbuffer, outbufferext,
+ stats->bbrtt.total.cnt,
+ (stats->bbrtt.total.mean * 1e3),
+ (stats->bbrtt.total.cnt < 2) ? 0 : (stats->bbrtt.total.min * 1e3),
+ (stats->bbrtt.total.cnt < 2) ? 0 : (stats->bbrtt.total.max * 1e3),
+ (stats->bbrtt.total.cnt < 2) ? 0 : 1e3 * (sqrt(stats->bbrtt.total.m2 / (stats->bbrtt.total.cnt - 1))),
+ stats->sock_callstats.write.tcpstats.retry,
+ stats->sock_callstats.write.tcpstats.cwnd,
+ stats->sock_callstats.write.tcpstats.rtt,
+ rps_string);
+#else
+ printf(report_client_bb_bw_format, stats->common->transferIDStr,
+ stats->ts.iStart, stats->ts.iEnd,
+ outbuffer, outbufferext,
+ stats->bbrtt.total.cnt,
+ (stats->bbrtt.total.mean * 1e3),
+ (stats->bbrtt.total.cnt < 2) ? 0 : (stats->bbrtt.total.min * 1e3),
+ (stats->bbrtt.total.cnt < 2) ? 0 : (stats->bbrtt.total.max * 1e3),
+ (stats->bbrtt.total.cnt < 2) ? 0 : 1e3 * (sqrt(stats->bbrtt.total.m2 / (stats->bbrtt.total.cnt - 1))),
+ rps_string);
+#endif
+ if (isTripTime(stats->common)) {
+ printf(report_client_bb_bw_triptime_format, stats->common->transferIDStr,
+ stats->ts.iStart, stats->ts.iEnd,
+ stats->bbowdto.total.cnt,
+ (stats->bbowdto.total.mean * 1e3),
+ (stats->bbowdto.total.cnt < 2) ? 0 : (stats->bbowdto.total.min * 1e3),
+ (stats->bbowdto.total.cnt < 2) ? 0 : (stats->bbowdto.total.max * 1e3),
+ (stats->bbowdto.total.cnt < 2) ? 0 : 1e3 * (sqrt(stats->bbowdto.total.m2 / (stats->bbowdto.total.cnt - 1))),
+ (stats->bbowdfro.total.mean * 1e3),
+ (stats->bbowdfro.total.cnt < 2) ? 0 : (stats->bbowdfro.total.min * 1e3),
+ (stats->bbowdfro.total.cnt < 2) ? 0 : (stats->bbowdfro.total.max * 1e3),
+ (stats->bbowdfro.total.cnt < 2) ? 0 : 1e3 * (sqrt(stats->bbowdfro.total.m2 / (stats->bbowdfro.total.cnt - 1))),
+ (stats->bbasym.total.mean * 1e3),
+ (stats->bbasym.total.cnt < 2) ? 0 : (stats->bbasym.total.min * 1e3),
+ (stats->bbasym.total.cnt < 2) ? 0 : (stats->bbasym.total.max * 1e3),
+ (stats->bbasym.total.cnt < 2) ? 0 : 1e3 * (sqrt(stats->bbasym.total.m2 / (stats->bbasym.total.cnt - 1))),
+ rps_string);
+ }
+ if (stats->bbrtt_histogram) {
+ histogram_print(stats->bbrtt_histogram, stats->ts.iStart, stats->ts.iEnd);
+ }
+ } else {
+ double rps = ((stats->bbrtt.current.cnt > 0) && (stats->iBBrunning > 0)) ? ((double) stats->bbrtt.current.cnt / stats->iBBrunning) : 0;
+ if (rps < 10)
+ snprintf(rps_string, sizeof(rps_string), "%0.1f", rps);
+ else
+ snprintf(rps_string, sizeof(rps_string), "%0.0f", rps);
+ rps_string[sizeof(rps_string) - 1] = '\0';
+
+#if HAVE_TCP_STATS
+ printf(report_client_bb_bw_format, stats->common->transferIDStr,
+ stats->ts.iStart, stats->ts.iEnd,
+ outbuffer, outbufferext,
+ stats->bbrtt.current.cnt,
+ (stats->bbrtt.current.mean * 1e3),
+ (stats->bbrtt.current.cnt < 2) ? 0 : (stats->bbrtt.current.min * 1e3),
+ (stats->bbrtt.current.cnt < 2) ? 0 : (stats->bbrtt.current.max * 1e3),
+ (stats->bbrtt.current.cnt < 2) ? 0 : 1e3 * (sqrt(stats->bbrtt.current.m2 / (stats->bbrtt.current.cnt - 1))),
+ stats->sock_callstats.write.tcpstats.retry,
+ stats->sock_callstats.write.tcpstats.cwnd,
+ stats->sock_callstats.write.tcpstats.rtt,
+ rps_string);
+#else
+ printf(report_client_bb_bw_format, stats->common->transferIDStr,
+ stats->ts.iStart, stats->ts.iEnd,
+ outbuffer, outbufferext,
+ stats->bbrtt.current.cnt,
+ (stats->bbrtt.current.mean * 1e3),
+ (stats->bbrtt.current.cnt < 2) ? 0 : (stats->bbrtt.current.min * 1e3),
+ (stats->bbrtt.current.cnt < 2) ? 0 : (stats->bbrtt.current.max * 1e3),
+ (stats->bbrtt.current.cnt < 2) ? 0 : 1e3 * (sqrt(stats->bbrtt.current.m2 / (stats->bbrtt.current.cnt - 1))),
+ rps_string);
+#endif
+ }
+ fflush(stdout);
+}
+
+void tcp_output_burst_write (struct TransferInfo *stats) {
+ HEADING_PRINT_COND(report_burst_write_tcp);
+ _print_stats_common(stats);
+#if HAVE_TCP_STATS
+ set_netpowerbuf((stats->transit.current.mean + stats->sock_callstats.write.tcpstats.rtt), stats);
+ printf(report_burst_write_tcp_format, stats->common->transferIDStr,
+ stats->ts.iStart, stats->ts.iEnd,
+ outbuffer, outbufferext,
+ stats->transit.current.mean,
+ stats->sock_callstats.write.WriteCnt,
+ stats->sock_callstats.write.WriteErr,
+ stats->sock_callstats.write.tcpstats.retry,
+ stats->sock_callstats.write.tcpstats.cwnd,
+ stats->sock_callstats.write.tcpstats.rtt,
+ netpower_buf);
+ #else
+ printf(report_burst_write_tcp_format, stats->common->transferIDStr,
+ stats->ts.iStart, stats->ts.iEnd,
+ outbuffer, outbufferext,
+ stats->transit.current.mean,
+ stats->sock_callstats.write.WriteCnt,
+ stats->sock_callstats.write.WriteErr);
+#endif
+ fflush(stdout);
+}
+
+void tcp_output_write_enhanced (struct TransferInfo *stats) {
+ HEADING_PRINT_COND(report_bw_write_enhanced);
+ _print_stats_common(stats);
+#if !(HAVE_TCP_STATS)
+ printf(report_bw_write_enhanced_format,
+ stats->common->transferIDStr, stats->ts.iStart, stats->ts.iEnd,
+ outbuffer, outbufferext,
+ stats->sock_callstats.write.WriteCnt,
+ stats->sock_callstats.write.WriteErr);
+#else
+ set_netpowerbuf(stats->sock_callstats.write.tcpstats.rtt * 1e-6, stats);
+ if (stats->sock_callstats.write.tcpstats.cwnd > 0) {
+ printf(report_bw_write_enhanced_format,
+ stats->common->transferIDStr, stats->ts.iStart, stats->ts.iEnd,
+ outbuffer, outbufferext,
+ stats->sock_callstats.write.WriteCnt,
+ stats->sock_callstats.write.WriteErr,
+ stats->sock_callstats.write.tcpstats.retry,
+ stats->sock_callstats.write.tcpstats.cwnd,
+ stats->sock_callstats.write.tcpstats.rtt,
+ stats->sock_callstats.write.tcpstats.rttvar,
+ netpower_buf);
+ } else {
+ printf(report_bw_write_enhanced_nocwnd_format,
+ stats->common->transferIDStr, stats->ts.iStart, stats->ts.iEnd,
+ outbuffer, outbufferext,
+ stats->sock_callstats.write.WriteCnt,
+ stats->sock_callstats.write.WriteErr,
+ stats->sock_callstats.write.tcpstats.retry,
+ stats->sock_callstats.write.tcpstats.rtt,
+ netpower_buf);
+ }
+#endif
+#if HAVE_DECL_TCP_NOTSENT_LOWAT
+ if (stats->latency_histogram) {
+ histogram_print(stats->latency_histogram, stats->ts.iStart, stats->ts.iEnd);
+ }
+#endif
+ fflush(stdout);
+}
+
+void tcp_output_write_enhanced_write (struct TransferInfo *stats) {
+ HEADING_PRINT_COND(report_write_enhanced_write);
+ _print_stats_common(stats);
+#if !(HAVE_TCP_STATS)
+ printf(report_write_enhanced_write_format,
+ stats->common->transferIDStr, stats->ts.iStart, stats->ts.iEnd,
+ outbuffer, outbufferext,
+ stats->sock_callstats.write.WriteCnt,
+ stats->sock_callstats.write.WriteErr,
+ stats->write_mmm.current.mean * 1e3,
+ stats->write_mmm.current.min * 1e3,
+ stats->write_mmm.current.max * 1e3,
+ (stats->write_mmm.current.cnt < 2) ? 0 : (1e-3 * sqrt(stats->write_mmm.current.m2 / (stats->write_mmm.current.cnt - 1))),
+ stats->write_mmm.current.cnt);
+#else
+ set_netpowerbuf(stats->sock_callstats.write.tcpstats.rtt * 1e-6, stats);
+ if (stats->sock_callstats.write.tcpstats.cwnd > 0) {
+ printf(report_write_enhanced_write_format,
+ stats->common->transferIDStr, stats->ts.iStart, stats->ts.iEnd,
+ outbuffer, outbufferext,
+ stats->sock_callstats.write.WriteCnt,
+ stats->sock_callstats.write.WriteErr,
+ stats->sock_callstats.write.tcpstats.retry,
+ stats->sock_callstats.write.tcpstats.cwnd,
+ stats->sock_callstats.write.tcpstats.rtt,
+ netpower_buf,
+ stats->write_mmm.current.mean * 1e-3,
+ stats->write_mmm.current.min * 1e-3,
+ stats->write_mmm.current.max * 1e-3,
+ (stats->write_mmm.current.cnt < 2) ? 0 : (1e-3 * sqrt(stats->write_mmm.current.m2 / (stats->write_mmm.current.cnt - 1))),
+ stats->write_mmm.current.cnt);
+ } else {
+ printf(report_write_enhanced_nocwnd_write_format,
+ stats->common->transferIDStr, stats->ts.iStart, stats->ts.iEnd,
+ outbuffer, outbufferext,
+ stats->sock_callstats.write.WriteCnt,
+ stats->sock_callstats.write.WriteErr,
+ stats->sock_callstats.write.tcpstats.retry,
+ stats->sock_callstats.write.tcpstats.rtt,
+ netpower_buf,
+ stats->write_mmm.current.mean * 1e3,
+ stats->write_mmm.current.min * 1e3,
+ stats->write_mmm.current.max * 1e3,
+ (stats->write_mmm.current.cnt < 2) ? 0 : (1e3 * sqrt(stats->write_mmm.current.m2 / (stats->write_mmm.current.cnt - 1))),
+ stats->write_mmm.current.cnt);
+ }
+#endif
+ if (stats->latency_histogram) {
+ histogram_print(stats->latency_histogram, stats->ts.iStart, stats->ts.iEnd);
+ }
+ if (stats->write_histogram) {
+ histogram_print(stats->write_histogram, stats->ts.iStart, stats->ts.iEnd);
+ }
+ fflush(stdout);
+}
+
+void tcp_output_write_enhanced_isoch (struct TransferInfo *stats) {
+ HEADING_PRINT_COND(report_write_enhanced_isoch);
+ _print_stats_common(stats);
+#if !(HAVE_TCP_STATS)
+ printf(report_write_enhanced_isoch_format,
+ stats->common->transferIDStr, stats->ts.iStart, stats->ts.iEnd,
+ outbuffer, outbufferext,
+ stats->sock_callstats.write.WriteCnt,
+ stats->sock_callstats.write.WriteErr,
+ stats->isochstats.cntFrames, stats->isochstats.cntFramesMissed, stats->isochstats.cntSlips);
+#else
+ set_netpowerbuf(stats->sock_callstats.write.tcpstats.rtt * 1e-6, stats);
+ if (stats->sock_callstats.write.tcpstats.cwnd > 0) {
+ printf(report_write_enhanced_isoch_format,
+ stats->common->transferIDStr, stats->ts.iStart, stats->ts.iEnd,
+ outbuffer, outbufferext,
+ stats->sock_callstats.write.WriteCnt,
+ stats->sock_callstats.write.WriteErr,
+ stats->sock_callstats.write.tcpstats.retry,
+ stats->sock_callstats.write.tcpstats.cwnd,
+ stats->sock_callstats.write.tcpstats.rtt,
+ stats->isochstats.cntFrames, stats->isochstats.cntFramesMissed, stats->isochstats.cntSlips,
+ netpower_buf);
+ } else {
+ printf(report_write_enhanced_isoch_nocwnd_format,
+ stats->common->transferIDStr, stats->ts.iStart, stats->ts.iEnd,
+ outbuffer, outbufferext,
+ stats->sock_callstats.write.WriteCnt,
+ stats->sock_callstats.write.WriteErr,
+ stats->sock_callstats.write.tcpstats.retry,
+ stats->sock_callstats.write.tcpstats.rtt,
+ stats->isochstats.cntFrames, stats->isochstats.cntFramesMissed, stats->isochstats.cntSlips,
+ netpower_buf);
+ }
+#endif
+#if HAVE_DECL_TCP_NOTSENT_LOWAT
+ if (stats->latency_histogram) {
+ histogram_print(stats->latency_histogram, stats->ts.iStart, stats->ts.iEnd);
+ }
+#endif
+ fflush(stdout);
+}
+
+
+//UDP output
+void udp_output_fullduplex (struct TransferInfo *stats) {
+ HEADING_PRINT_COND(report_udp_fullduplex);
+ _print_stats_common(stats);
+ printf(report_udp_fullduplex_format, stats->common->transferIDStr, stats->ts.iStart, stats->ts.iEnd, outbuffer, outbufferext, \
+ stats->cntDatagrams, (stats->cntIPG && (stats->IPGsum > 0.0) ? (stats->cntIPG / stats->IPGsum) : 0.0));
+ fflush(stdout);
+}
+
+void udp_output_fullduplex_enhanced (struct TransferInfo *stats) {
+ HEADING_PRINT_COND(report_udp_fullduplex);
+ _print_stats_common(stats);
+ printf(report_udp_fullduplex_enhanced_format, stats->common->transferID, stats->ts.iStart, stats->ts.iEnd, outbuffer, outbufferext, \
+ stats->cntDatagrams, (stats->cntIPG && (stats->IPGsum > 0.0) ? (stats->cntIPG / stats->IPGsum) : 0.0));
+ fflush(stdout);
+}
+
+void udp_output_fullduplex_sum (struct TransferInfo *stats) {
+ HEADING_PRINT_COND(report_udp_fullduplex);
+ _print_stats_common(stats);
+ printf(report_udp_fullduplex_sum_format, stats->ts.iStart, stats->ts.iEnd, outbuffer, outbufferext, \
+ stats->cntDatagrams, (stats->cntIPG && (stats->IPGsum > 0.0) ? (stats->cntIPG / stats->IPGsum) : 0.0));
+ fflush(stdout);
+}
+
+
+void udp_output_read (struct TransferInfo *stats) {
+ HEADING_PRINT_COND(report_bw_jitter_loss);
+ _print_stats_common(stats);
+ printf(report_bw_jitter_loss_format, stats->common->transferIDStr,
+ stats->ts.iStart, stats->ts.iEnd,
+ outbuffer, outbufferext,
+ (stats->final) ? ((stats->inline_jitter.total.sum / (double) stats->inline_jitter.total.cnt) * 1e3) : (stats->jitter * 1e3), \
+ stats->cntError, stats->cntDatagrams,
+ (100.0 * stats->cntError) / stats->cntDatagrams);
+ _output_outoforder(stats);
+ fflush(stdout);
+}
+
+void udp_output_read_triptime (struct TransferInfo *stats) {
+ HEADING_PRINT_COND(report_bw_jitter_loss_enhanced_triptime);
+ _print_stats_common(stats);
+ if (!stats->cntIPG) {
+ printf(report_bw_jitter_loss_suppress_enhanced_format, stats->common->transferIDStr,
+ stats->ts.iStart, stats->ts.iEnd,
+ outbuffer, outbufferext,
+ 0.0, stats->cntError,
+ stats->cntDatagrams,
+ 0.0,0.0,0.0,0.0,0.0,0.0);
+ } else {
+ if ((stats->transit.current.min > UNREALISTIC_LATENCYMINMAX) ||
+ (stats->transit.current.min < UNREALISTIC_LATENCYMINMIN)) {
+ printf(report_bw_jitter_loss_suppress_enhanced_format, stats->common->transferIDStr,
+ stats->ts.iStart, stats->ts.iEnd,
+ outbuffer, outbufferext,
+ (stats->final) ? ((stats->inline_jitter.total.sum / (double) stats->inline_jitter.total.cnt) * 1e3) : (stats->jitter * 1e3),
+ stats->cntError, stats->cntDatagrams,
+ (100.0 * stats->cntError) / stats->cntDatagrams,
+ (stats->cntIPG / stats->IPGsum));
+ } else {
+ double meantransit;
+ double variance;
+ char llaw_bufstr[LLAWBUFSIZE];
+ int lambda = ((stats->IPGsum > 0.0) ? (round (stats->cntIPG / stats->IPGsum)) : 0.0);
+ if (!stats->final) {
+ meantransit = (stats->transit.current.cnt > 0) ? (stats->transit.current.sum / stats->transit.current.cnt) : 0;
+ variance = (stats->transit.current.cnt < 2) ? 0 : \
+ (sqrt(stats->transit.current.m2 / (stats->transit.current.cnt - 1)));
+ snprintf(llaw_bufstr, sizeof(llaw_bufstr), "%.0f(%.0f) pkts", stats->iInP, ((double) lambda * variance));
+ } else {
+ meantransit = (stats->transit.total.cnt > 0) ? (stats->transit.total.sum / stats->transit.total.cnt) : 0;
+ variance = (stats->transit.total.cnt < 2) ? 0 : \
+ (sqrt(stats->transit.total.m2 / (stats->transit.total.cnt - 1)));
+ snprintf(llaw_bufstr, sizeof(llaw_bufstr), "%.0f(%.0f) pkts", stats->fInP, ((double) lambda * variance));
+ }
+ llaw_bufstr[sizeof(llaw_bufstr)-1] = '\0';
+ set_netpowerbuf(meantransit, stats);
+ printf(report_bw_jitter_loss_enhanced_triptime_format, stats->common->transferIDStr,
+ stats->ts.iStart, stats->ts.iEnd,
+ outbuffer, outbufferext,
+ (stats->final) ? ((stats->inline_jitter.total.sum / (double) stats->inline_jitter.total.cnt) * 1e3) : (stats->jitter * 1e3), \
+ stats->cntError, stats->cntDatagrams,
+ (100.0 * stats->cntError) / stats->cntDatagrams,
+ (meantransit * 1e3),
+ ((stats->final ? stats->transit.total.min : stats->transit.current.min) * 1e3),
+ ((stats->final ? stats->transit.total.max : stats->transit.current.max) * 1e3),
+ (stats->final ? (stats->transit.total.cnt < 2) : (stats->transit.current.cnt < 2)) ? 0 : (1e3 * variance), // convert from sec to ms
+ (stats->cntIPG / stats->IPGsum),
+ stats->cntIPG,
+ llaw_bufstr,
+ netpower_buf);
+ }
+ }
+ if (stats->latency_histogram) {
+ histogram_print(stats->latency_histogram, stats->ts.iStart, stats->ts.iEnd);
+ }
+ if (stats->jitter_histogram) {
+ histogram_print(stats->jitter_histogram, stats->ts.iStart, stats->ts.iEnd);
+ }
+ _output_outoforder(stats);
+ fflush(stdout);
+}
+void udp_output_read_enhanced (struct TransferInfo *stats) {
+ HEADING_PRINT_COND(report_bw_jitter_loss_enhanced);
+ _print_stats_common(stats);
+ if (!stats->cntIPG) {
+ printf(report_bw_jitter_loss_suppress_enhanced_format, stats->common->transferIDStr,
+ stats->ts.iStart, stats->ts.iEnd,
+ outbuffer, outbufferext,
+ 0.0, stats->cntError,
+ stats->cntDatagrams,
+ 0.0,0.0,0.0,0.0,0.0,0.0);
+ } else {
+ if ((stats->transit.current.min > UNREALISTIC_LATENCYMINMAX) ||
+ (stats->transit.current.min < UNREALISTIC_LATENCYMINMIN)) {
+ printf(report_bw_jitter_loss_suppress_enhanced_format, stats->common->transferIDStr,
+ stats->ts.iStart, stats->ts.iEnd,
+ outbuffer, outbufferext,
+ (stats->final) ? ((stats->inline_jitter.total.sum / (double) stats->inline_jitter.total.cnt) * 1e3) : (stats->jitter * 1e3),
+ stats->cntError, stats->cntDatagrams,
+ (100.0 * stats->cntError) / stats->cntDatagrams,
+ (stats->cntIPG / stats->IPGsum));
+ } else {
+ double meantransit;
+ double variance;
+ if (!stats->final) {
+ meantransit = (stats->transit.current.cnt > 0) ? (stats->transit.current.sum / stats->transit.current.cnt) : 0;
+ variance = (stats->transit.current.cnt < 2) ? 0 : \
+ (sqrt(stats->transit.current.m2 / (stats->transit.current.cnt - 1)));
+ } else {
+ meantransit = (stats->transit.total.cnt > 0) ? (stats->transit.total.sum / stats->transit.total.cnt) : 0;
+ variance = (stats->transit.total.cnt < 2) ? 0 : \
+ (sqrt(stats->transit.total.m2 / (stats->transit.total.cnt - 1)));
+ }
+ set_netpowerbuf(meantransit, stats);
+ printf(report_bw_jitter_loss_enhanced_format, stats->common->transferIDStr,
+ stats->ts.iStart, stats->ts.iEnd,
+ outbuffer, outbufferext,
+ (stats->final) ? ((stats->inline_jitter.total.sum / (double) stats->inline_jitter.total.cnt) * 1e3) : (stats->jitter * 1e3), \
+ stats->cntError, stats->cntDatagrams,
+ (100.0 * stats->cntError) / stats->cntDatagrams,
+ (meantransit * 1e3),
+ ((stats->final ? stats->transit.total.min : stats->transit.current.min) * 1e3),
+ ((stats->final ? stats->transit.total.max : stats->transit.current.max) * 1e3),
+ (stats->final ? (stats->transit.total.cnt < 2) : (stats->transit.current.cnt < 2)) ? 0 : (1e3 * variance), // convert from sec to ms
+ (stats->cntIPG / stats->IPGsum),
+ netpower_buf);
+ }
+ }
+ if (stats->latency_histogram) {
+ histogram_print(stats->latency_histogram, stats->ts.iStart, stats->ts.iEnd);
+ }
+ if (stats->jitter_histogram) {
+ histogram_print(stats->jitter_histogram, stats->ts.iStart, stats->ts.iEnd);
+ }
+ _output_outoforder(stats);
+ fflush(stdout);
+}
+void udp_output_read_triptime_isoch (struct TransferInfo *stats) {
+ HEADING_PRINT_COND(report_bw_jitter_loss_enhanced_isoch_triptime);
+ _print_stats_common(stats);
+ if (!stats->cntIPG) {
+ printf(report_bw_jitter_loss_suppress_enhanced_format, stats->common->transferIDStr,
+ stats->ts.iStart, stats->ts.iEnd,
+ outbuffer, outbufferext,
+ 0.0, stats->cntError,
+ stats->cntDatagrams,
+ 0.0,0.0,0.0,0.0,0.0,0.0);
+
+ } else {
+ // If the min latency is out of bounds of a realistic value
+ // assume the clocks are not synched and suppress the
+ // latency output
+ if ((stats->transit.current.min > UNREALISTIC_LATENCYMINMAX) ||
+ (stats->transit.current.min < UNREALISTIC_LATENCYMINMIN)) {
+ printf(report_bw_jitter_loss_suppress_enhanced_format, stats->common->transferIDStr,
+ stats->ts.iStart, stats->ts.iEnd,
+ outbuffer, outbufferext,
+ (stats->final) ? ((stats->inline_jitter.total.sum / (double) stats->inline_jitter.total.cnt) * 1e3) : (stats->jitter * 1e3),
+ stats->cntError, stats->cntDatagrams,
+ (100.0 * stats->cntError) / stats->cntDatagrams,
+ (stats->cntIPG / stats->IPGsum));
+ } else {
+ double frame_meantransit = (stats->isochstats.transit.current.cnt > 0) ? (stats->isochstats.transit.current.sum / stats->isochstats.transit.current.cnt) : 0;
+ double meantransit = (stats->transit.current.cnt > 0) ? (stats->transit.current.sum / stats->transit.current.cnt) : 0;
+ set_netpowerbuf(meantransit, stats);
+ printf(report_bw_jitter_loss_enhanced_isoch_format, stats->common->transferIDStr,
+ stats->ts.iStart, stats->ts.iEnd,
+ outbuffer, outbufferext,
+ (stats->final) ? ((stats->inline_jitter.total.sum / (double) stats->inline_jitter.total.cnt) * 1e3) : (stats->jitter * 1e3), \
+ stats->cntError, stats->cntDatagrams,
+ (100.0 * stats->cntError) / stats->cntDatagrams,
+ (meantransit * 1e3),
+ stats->transit.current.min * 1e3,
+ stats->transit.current.max * 1e3,
+ (stats->transit.current.cnt < 2) ? 0 : 1e3 * (sqrt(stats->transit.current.m2 / (stats->transit.current.cnt - 1))),
+ (stats->cntIPG / stats->IPGsum),
+ stats->isochstats.cntFrames, stats->isochstats.cntFramesMissed,
+ (frame_meantransit * 1e3),
+ stats->isochstats.transit.current.min * 1e3,
+ stats->isochstats.transit.current.max * 1e3,
+ (stats->isochstats.transit.current.cnt < 2) ? 0 : 1e3 * (sqrt(stats->isochstats.transit.current.m2 / (stats->isochstats.transit.current.cnt - 1))),
+ netpower_buf);
+#if 0
+ if (stats->final) {
+ printf("***** Jitter MMM = %f/%f/%f\n",stats->inline_jitter.total.mean, stats->inline_jitter.total.min, stats->inline_jitter.total.max);
+ }
+#endif
+ }
+ }
+ if (stats->latency_histogram) {
+ histogram_print(stats->latency_histogram, stats->ts.iStart, stats->ts.iEnd);
+ }
+ if (stats->jitter_histogram) {
+ histogram_print(stats->jitter_histogram, stats->ts.iStart, stats->ts.iEnd);
+ }
+ if (stats->framelatency_histogram) {
+ histogram_print(stats->framelatency_histogram, stats->ts.iStart, stats->ts.iEnd);
+ }
+ _output_outoforder(stats);
+ fflush(stdout);
+}
+void udp_output_write (struct TransferInfo *stats) {
+ HEADING_PRINT_COND(report_bw);
+ _print_stats_common(stats);
+ printf(report_bw_format, stats->common->transferIDStr,
+ stats->ts.iStart, stats->ts.iEnd,
+ outbuffer, outbufferext);
+ fflush(stdout);
+}
+
+void udp_output_write_enhanced (struct TransferInfo *stats) {
+ HEADING_PRINT_COND(report_bw_pps_enhanced);
+ _print_stats_common(stats);
+ printf(report_bw_pps_enhanced_format, stats->common->transferIDStr,
+ stats->ts.iStart, stats->ts.iEnd,
+ outbuffer, outbufferext,
+ stats->sock_callstats.write.WriteCnt,
+ stats->sock_callstats.write.WriteErr,
+ (stats->cntIPG ? (stats->cntIPG / stats->IPGsum) : 0.0));
+ fflush(stdout);
+}
+void udp_output_write_enhanced_isoch (struct TransferInfo *stats) {
+ HEADING_PRINT_COND(report_bw_pps_enhanced_isoch);
+ _print_stats_common(stats);
+ printf(report_bw_pps_enhanced_isoch_format, stats->common->transferIDStr,
+ stats->ts.iStart, stats->ts.iEnd,
+ outbuffer, outbufferext,
+ stats->sock_callstats.write.WriteCnt,
+ stats->sock_callstats.write.WriteErr,
+ (stats->cntIPG ? (stats->cntIPG / stats->IPGsum) : 0.0),
+ stats->isochstats.cntFrames, stats->isochstats.cntFramesMissed, stats->isochstats.cntSlips);
+ fflush(stdout);
+}
+
+// Sum reports
+void udp_output_sum_read (struct TransferInfo *stats) {
+ HEADING_PRINT_COND(report_bw);
+ _print_stats_common(stats);
+ printf(report_sum_bw_jitter_loss_format,
+ stats->ts.iStart, stats->ts.iEnd,
+ outbuffer, outbufferext,
+ stats->cntError, stats->cntDatagrams,
+ (100.0 * stats->cntError) / stats->cntDatagrams);
+ if ((stats->cntOutofOrder > 0) && stats->final) {
+ printf(report_sum_outoforder,
+ stats->ts.iStart,
+ stats->ts.iEnd, stats->cntOutofOrder);
+ }
+ fflush(stdout);
+}
+void udp_output_sumcnt (struct TransferInfo *stats) {
+ HEADING_PRINT_COND(report_sumcnt_bw);
+ _print_stats_common(stats);
+ printf(report_sumcnt_bw_format, stats->threadcnt,
+ stats->ts.iStart, stats->ts.iEnd,
+ outbuffer, outbufferext);
+ if ((stats->cntOutofOrder > 0) && stats->final) {
+ if (isSumOnly(stats->common)) {
+ printf(report_sumcnt_outoforder,
+ stats->threadcnt,
+ stats->ts.iStart,
+ stats->ts.iEnd, stats->cntOutofOrder);
+ } else {
+ printf(report_outoforder,
+ stats->common->transferIDStr, stats->ts.iStart,
+ stats->ts.iEnd, stats->cntOutofOrder);
+ }
+ }
+ fflush(stdout);
+}
+void udp_output_sumcnt_enhanced (struct TransferInfo *stats) {
+ HEADING_PRINT_COND(report_sumcnt_bw_jitter_loss);
+ _print_stats_common(stats);
+ printf(report_sumcnt_bw_jitter_loss_format, stats->threadcnt, stats->ts.iStart, stats->ts.iEnd, outbuffer, outbufferext, \
+ stats->cntError, stats->cntDatagrams, (stats->cntIPG && (stats->IPGsum > 0.0) ? (stats->cntIPG / stats->IPGsum) : 0.0));
+ if ((stats->cntOutofOrder > 0) && stats->final) {
+ if (isSumOnly(stats->common)) {
+ printf(report_sumcnt_outoforder,
+ stats->threadcnt,
+ stats->ts.iStart,
+ stats->ts.iEnd, stats->cntOutofOrder);
+ } else {
+ printf(report_sum_outoforder,
+ stats->ts.iStart,
+ stats->ts.iEnd, stats->cntOutofOrder);
+ }
+ }
+ fflush(stdout);
+}
+
+void udp_output_sumcnt_read_enhanced (struct TransferInfo *stats) {
+ HEADING_PRINT_COND(report_sumcnt_bw_read_enhanced);
+ _print_stats_common(stats);
+ printf(report_sumcnt_bw_read_enhanced_format, stats->threadcnt,
+ stats->ts.iStart, stats->ts.iEnd,
+ outbuffer, outbufferext,
+ (stats->final) ? ((stats->inline_jitter.total.sum / (double) stats->inline_jitter.total.cnt) * 1e3) : (stats->jitter * 1e3), \
+ stats->cntError, stats->cntDatagrams,
+ (100.0 * stats->cntError) / stats->cntDatagrams);
+ if ((stats->cntOutofOrder > 0) && stats->final) {
+ if (isSumOnly(stats->common)) {
+ printf(report_sumcnt_outoforder,
+ stats->threadcnt,
+ stats->ts.iStart,
+ stats->ts.iEnd, stats->cntOutofOrder);
+ } else {
+ printf(report_sum_outoforder,
+ stats->ts.iStart,
+ stats->ts.iEnd, stats->cntOutofOrder);
+ }
+ }
+ fflush(stdout);
+}
+
+void udp_output_sumcnt_read_triptime (struct TransferInfo *stats) {
+ HEADING_PRINT_COND(report_sumcnt_udp_triptime);
+ _print_stats_common(stats);
+ printf(report_sumcnt_udp_triptime_format, stats->threadcnt, stats->ts.iStart, stats->ts.iEnd, outbuffer, outbufferext, \
+ stats->cntError, stats->cntDatagrams, stats->cntIPG, (stats->final ? stats->fInP : stats->iInP), (stats->cntIPG && (stats->IPGsum > 0.0) ? (stats->cntIPG / stats->IPGsum) : 0.0));
+ if ((stats->cntOutofOrder > 0) && stats->final) {
+ if (isSumOnly(stats->common)) {
+ printf(report_sumcnt_outoforder,
+ stats->threadcnt,
+ stats->ts.iStart,
+ stats->ts.iEnd, stats->cntOutofOrder);
+ } else {
+ printf(report_sum_outoforder,
+ stats->ts.iStart,
+ stats->ts.iEnd, stats->cntOutofOrder);
+ }
+ }
+ fflush(stdout);
+}
+
+void udp_output_sum_write (struct TransferInfo *stats) {
+ HEADING_PRINT_COND(report_bw);
+ _print_stats_common(stats);
+ printf(report_sum_bw_format, stats->ts.iStart, stats->ts.iEnd,
+ outbuffer, outbufferext);
+ fflush(stdout);
+}
+void udp_output_sumcnt_write (struct TransferInfo *stats) {
+ HEADING_PRINT_COND(report_sumcnt_bw);
+ _print_stats_common(stats);
+ printf(report_sumcnt_bw_format, stats->threadcnt,
+ stats->ts.iStart, stats->ts.iEnd,
+ outbuffer, outbufferext);
+ fflush(stdout);
+}
+void udp_output_sum_read_enhanced (struct TransferInfo *stats) {
+ HEADING_PRINT_COND(report_sumcnt_udp_enhanced);
+ _print_stats_common(stats);
+ printf(report_sumcnt_udp_enhanced_format, stats->threadcnt,
+ stats->ts.iStart, stats->ts.iEnd,
+ outbuffer, outbufferext,
+ stats->cntError, stats->cntDatagrams,
+ (stats->cntIPG ? (stats->cntIPG / stats->IPGsum) : 0.0));
+ if (stats->latency_histogram && stats->final) {
+ histogram_print(stats->latency_histogram, stats->ts.iStart, stats->ts.iEnd);
+ }
+ if (stats->jitter_histogram && stats->final) {
+ histogram_print(stats->jitter_histogram, stats->ts.iStart, stats->ts.iEnd);
+ }
+ if ((stats->cntOutofOrder > 0) && stats->final) {
+ if (isSumOnly(stats->common)) {
+ printf(report_sumcnt_outoforder,
+ stats->threadcnt,
+ stats->ts.iStart,
+ stats->ts.iEnd, stats->cntOutofOrder);
+ } else {
+ printf(report_sum_outoforder,
+ stats->ts.iStart,
+ stats->ts.iEnd, stats->cntOutofOrder);
+ }
+ }
+ fflush(stdout);
+}
+void udp_output_sum_write_enhanced (struct TransferInfo *stats) {
+ HEADING_PRINT_COND(report_bw_pps_enhanced);
+ _print_stats_common(stats);
+ printf(report_sum_bw_pps_enhanced_format,
+ stats->ts.iStart, stats->ts.iEnd,
+ outbuffer, outbufferext,
+ stats->sock_callstats.write.WriteCnt,
+ stats->sock_callstats.write.WriteErr,
+ ((stats->cntIPG && (stats->IPGsum > 0.0)) ? (stats->cntIPG / stats->IPGsum) : 0.0));
+ fflush(stdout);
+}
+void udp_output_sumcnt_write_enhanced (struct TransferInfo *stats) {
+ HEADING_PRINT_COND(report_sumcnt_bw_pps_enhanced);
+ _print_stats_common(stats);
+ printf(report_sumcnt_bw_pps_enhanced_format, stats->threadcnt,
+ stats->ts.iStart, stats->ts.iEnd,
+ outbuffer, outbufferext,
+ stats->sock_callstats.write.WriteCnt,
+ stats->sock_callstats.write.WriteErr,
+ ((stats->cntIPG && (stats->IPGsum > 0.0)) ? (stats->cntIPG / stats->IPGsum) : 0.0));
+ fflush(stdout);
+}
+
+void tcp_output_sum_read (struct TransferInfo *stats) {
+ HEADING_PRINT_COND(report_bw);
+ _print_stats_common(stats);
+ printf(report_sum_bw_format,
+ stats->ts.iStart, stats->ts.iEnd,
+ outbuffer, outbufferext);
+ fflush(stdout);
+}
+void tcp_output_sum_read_enhanced (struct TransferInfo *stats) {
+ HEADING_PRINT_COND(report_bw_read_enhanced);
+ _print_stats_common(stats);
+ printf(report_sum_bw_read_enhanced_format,
+ stats->ts.iStart, stats->ts.iEnd,
+ outbuffer, outbufferext,
+ stats->sock_callstats.read.cntRead,
+ stats->sock_callstats.read.bins[0],
+ stats->sock_callstats.read.bins[1],
+ stats->sock_callstats.read.bins[2],
+ stats->sock_callstats.read.bins[3],
+ stats->sock_callstats.read.bins[4],
+ stats->sock_callstats.read.bins[5],
+ stats->sock_callstats.read.bins[6],
+ stats->sock_callstats.read.bins[7]);
+ if (stats->framelatency_histogram && stats->final) {
+ histogram_print(stats->framelatency_histogram, stats->ts.iStart, stats->ts.iEnd);
+ }
+ fflush(stdout);
+}
+void tcp_output_sumcnt_read (struct TransferInfo *stats) {
+ HEADING_PRINT_COND(report_sumcnt_bw);
+ _print_stats_common(stats);
+ printf(report_sumcnt_bw_format, stats->threadcnt,
+ stats->ts.iStart, stats->ts.iEnd,
+ outbuffer, outbufferext);
+ fflush(stdout);
+}
+void tcp_output_sumcnt_read_enhanced (struct TransferInfo *stats) {
+ HEADING_PRINT_COND(report_sumcnt_bw_read_enhanced);
+ _print_stats_common(stats);
+ printf(report_sumcnt_bw_read_enhanced_format, stats->threadcnt,
+ stats->ts.iStart, stats->ts.iEnd,
+ outbuffer, outbufferext,
+ stats->sock_callstats.read.cntRead,
+ stats->sock_callstats.read.bins[0],
+ stats->sock_callstats.read.bins[1],
+ stats->sock_callstats.read.bins[2],
+ stats->sock_callstats.read.bins[3],
+ stats->sock_callstats.read.bins[4],
+ stats->sock_callstats.read.bins[5],
+ stats->sock_callstats.read.bins[6],
+ stats->sock_callstats.read.bins[7]);
+ fflush(stdout);
+}
+void tcp_output_sumcnt_read_triptime (struct TransferInfo *stats) {
+ HEADING_PRINT_COND(report_sumcnt_bw_read_triptime);
+ _print_stats_common(stats);
+ char llaw_bufstr[LLAWBUFSIZE];
+ human_format_llawbuf(llaw_bufstr, sizeof(llaw_bufstr), ((stats->final) ? stats->fInP : stats->iInP));
+ printf(report_sumcnt_bw_read_triptime_format, stats->threadcnt,
+ stats->ts.iStart, stats->ts.iEnd,
+ outbuffer, outbufferext,
+ llaw_bufstr,
+ stats->sock_callstats.read.cntRead,
+ stats->sock_callstats.read.bins[0],
+ stats->sock_callstats.read.bins[1],
+ stats->sock_callstats.read.bins[2],
+ stats->sock_callstats.read.bins[3],
+ stats->sock_callstats.read.bins[4],
+ stats->sock_callstats.read.bins[5],
+ stats->sock_callstats.read.bins[6],
+ stats->sock_callstats.read.bins[7]);
+ fflush(stdout);
+}
+
+void tcp_output_sum_write (struct TransferInfo *stats) {
+ HEADING_PRINT_COND(report_bw);
+ _print_stats_common(stats);
+ printf(report_sum_bw_format,
+ stats->ts.iStart, stats->ts.iEnd,
+ outbuffer, outbufferext);
+ fflush(stdout);
+}
+void tcp_output_sumcnt_write (struct TransferInfo *stats) {
+ HEADING_PRINT_COND(report_sumcnt_bw);
+ _print_stats_common(stats);
+ printf(report_sumcnt_bw_format, stats->threadcnt,
+ stats->ts.iStart, stats->ts.iEnd,
+ outbuffer, outbufferext);
+ fflush(stdout);
+}
+void tcp_output_sum_write_enhanced (struct TransferInfo *stats) {
+ HEADING_PRINT_COND(report_bw_write_enhanced);
+ _print_stats_common(stats);
+ printf(report_sum_bw_write_enhanced_format,
+ stats->ts.iStart, stats->ts.iEnd,
+ outbuffer, outbufferext,
+ stats->sock_callstats.write.WriteCnt,
+ stats->sock_callstats.write.WriteErr
+#if HAVE_TCP_STATS
+ ,stats->sock_callstats.write.tcpstats.retry
+#endif
+ );
+ fflush(stdout);
+}
+void tcp_output_sumcnt_write_enhanced (struct TransferInfo *stats) {
+ HEADING_PRINT_COND(report_sumcnt_bw_write_enhanced);
+ _print_stats_common(stats);
+ printf(report_sumcnt_bw_write_enhanced_format, stats->threadcnt,
+ stats->ts.iStart, stats->ts.iEnd,
+ outbuffer, outbufferext,
+ stats->sock_callstats.write.WriteCnt,
+ stats->sock_callstats.write.WriteErr
+#if HAVE_TCP_STATS
+ ,stats->sock_callstats.write.tcpstats.retry
+#endif
+ );
+ fflush(stdout);
+}
+
+// CSV outputs
+void format_ips_port_string (struct TransferInfo *stats, bool sum) {
+ char local_addr[REPORT_ADDRLEN];
+ char remote_addr[REPORT_ADDRLEN];
+ uint16_t local_port;
+ uint16_t remote_port;
+ int swap = (stats->common->ThreadMode == kMode_Server);
+ int reverse = (isServerReverse(stats->common) || isReverse(stats->common));
+ struct sockaddr *local = (swap ? (struct sockaddr*)&stats->common->peer : (struct sockaddr*)&stats->common->local);
+ struct sockaddr *peer = (swap ? (struct sockaddr*)&stats->common->local : (struct sockaddr*)&stats->common->peer);
+
+ if (local->sa_family == AF_INET) {
+ if (isHideIPs(stats->common)) {
+ inet_ntop_hide(AF_INET, &((struct sockaddr_in*)local)->sin_addr,
+ local_addr, REPORT_ADDRLEN);
+ } else {
+ inet_ntop(AF_INET, &((struct sockaddr_in*)local)->sin_addr,
+ local_addr, REPORT_ADDRLEN);
+ }
+ if (!reverse && sum)
+ local_port = 0;
+ else
+ local_port = ntohs(((struct sockaddr_in*)local)->sin_port);
+ } else {
+#if HAVE_IPV6
+ if (local->sa_family == AF_INET6) {
+ inet_ntop(AF_INET6, &((struct sockaddr_in6*)local)->sin6_addr,
+ local_addr, REPORT_ADDRLEN);
+ if (swap && sum)
+ local_port = 0;
+ else
+ local_port = ntohs(((struct sockaddr_in6*)local)->sin6_port);
+ } else
+#endif
+ {
+ local_addr[0] = '\0';
+ local_port = 0;
+ }
+ }
+
+ if (peer->sa_family == AF_INET) {
+ if (isHideIPs(stats->common)) {
+ inet_ntop_hide(AF_INET, &((struct sockaddr_in*)peer)->sin_addr,
+ remote_addr, REPORT_ADDRLEN);
+ } else {
+ inet_ntop(AF_INET, &((struct sockaddr_in*)peer)->sin_addr,
+ remote_addr, REPORT_ADDRLEN);
+ }
+ if (reverse && sum)
+ remote_port = 0;
+ else
+ remote_port = ntohs(((struct sockaddr_in*)peer)->sin_port);
+ } else {
+#if HAVE_IPV6
+ if (local->sa_family == AF_INET6) {
+ inet_ntop(AF_INET6, &((struct sockaddr_in6*)peer)->sin6_addr,
+ remote_addr, REPORT_ADDRLEN);
+ if (!swap && sum)
+ remote_port = 0;
+ else
+ remote_port = ntohs(((struct sockaddr_in6*)peer)->sin6_port);
+ } else
+#endif
+ {
+ remote_addr[0] = '\0';
+ remote_port = 0;
+ }
+ }
+
+ snprintf((char *)&stats->csv_peer, CSVPEERLIMIT, reportCSV_peer,
+ local_addr, local_port,
+ remote_addr, remote_port);
+ stats->csv_peer[(CSVPEERLIMIT-1)] = '\0';
+#if 0 // use to debug CSV ouput
+ printf("*** output = %s swap=%d reverse=%d sum=%d\n", stats->csv_peer, swap, reverse, sum);
+#endif
+}
+
+void udp_output_basic_csv (struct TransferInfo *stats) {
+ char timestr[120];
+ iperf_formattime(timestr, sizeof(timestr), (!stats->final ? stats->ts.nextTime : stats->ts.packetTime), \
+ isEnhanced(stats->common), isUTC(stats->common), (isEnhanced(stats->common) ? CSVTZ : CSV));
+ intmax_t speed = (intmax_t) (((stats->cntBytes > 0) && (stats->ts.iEnd - stats->ts.iStart) > 0.0) ? \
+ (((double)stats->cntBytes * 8.0) / (stats->ts.iEnd - stats->ts.iStart)) : 0);
+ printf(reportCSV_bw_jitter_loss_format,
+ timestr,
+ stats->csv_peer,
+ stats->common->transferID,
+ stats->ts.iStart,
+ stats->ts.iEnd,
+ stats->cntBytes,
+ speed,
+ (stats->final) ? ((stats->inline_jitter.total.sum / (double) stats->inline_jitter.total.cnt) * 1e3) : (stats->jitter * 1e3),
+ stats->cntError,
+ stats->cntDatagrams,
+ (100.0 * stats->cntError) / stats->cntDatagrams, stats->cntOutofOrder );
+}
+
+void udp_output_enhanced_csv (struct TransferInfo *stats) {
+ char timestr[80];
+ iperf_formattime(timestr, sizeof(timestr), (!stats->final ? stats->ts.nextTime : stats->ts.packetTime), \
+ isEnhanced(stats->common), isUTC(stats->common),
+ isEnhanced(stats->common) ? CSVTZ : CSV);
+ intmax_t speed = (intmax_t) (((stats->cntBytes > 0) && (stats->ts.iEnd - stats->ts.iStart) > 0.0) ? \
+ (((double)stats->cntBytes * 8.0) / (stats->ts.iEnd - stats->ts.iStart)) : 0);
+ printf(reportCSV_bw_jitter_loss_pps_format,
+ timestr,
+ stats->csv_peer,
+ stats->common->transferID,
+ stats->ts.iStart,
+ stats->ts.iEnd,
+ stats->cntBytes,
+ speed,
+ (stats->jitter * 1e3),
+ stats->cntError,
+ stats->cntDatagrams,
+ (100.0 * stats->cntError) / stats->cntDatagrams,
+ stats->cntOutofOrder,
+ stats->sock_callstats.write.WriteCnt,
+ stats->sock_callstats.write.WriteErr,
+ (stats->cntIPG ? (stats->cntIPG / stats->IPGsum) : 0.0));
+}
+
+void tcp_output_basic_csv (struct TransferInfo *stats) {
+ char timestr[80];
+ iperf_formattime(timestr, sizeof(timestr), (!stats->final ? stats->ts.nextTime : stats->ts.packetTime), \
+ isEnhanced(stats->common), isUTC(stats->common), (isEnhanced(stats->common) ? CSVTZ : CSV));
+ intmax_t speed = (intmax_t) (((stats->cntBytes > 0) && (stats->ts.iEnd - stats->ts.iStart) > 0.0) ? \
+ (((double)stats->cntBytes * 8.0) / (stats->ts.iEnd - stats->ts.iStart)) : 0);
+ printf(reportCSV_bw_format,
+ timestr,
+ stats->csv_peer,
+ stats->common->transferID,
+ stats->ts.iStart,
+ stats->ts.iEnd,
+ stats->cntBytes,
+ speed);
+}
+
+void tcp_output_read_enhanced_csv (struct TransferInfo *stats) {
+ char timestr[80];
+ iperf_formattime(timestr, sizeof(timestr), (!stats->final ? stats->ts.nextTime : stats->ts.packetTime), \
+ isEnhanced(stats->common), isUTC(stats->common),
+ isEnhanced(stats->common) ? CSVTZ : CSV);
+ intmax_t speed = (intmax_t) (((stats->cntBytes > 0) && (stats->ts.iEnd - stats->ts.iStart) > 0.0) ? \
+ (((double)stats->cntBytes * 8.0) / (stats->ts.iEnd - stats->ts.iStart)) : 0);
+ printf(reportCSV_bw_read_enhanced_format,
+ timestr,
+ stats->csv_peer,
+ stats->common->transferID,
+ stats->ts.iStart,
+ stats->ts.iEnd,
+ stats->cntBytes,
+ speed,
+ stats->sock_callstats.read.cntRead,
+ stats->sock_callstats.read.bins[0],
+ stats->sock_callstats.read.bins[1],
+ stats->sock_callstats.read.bins[2],
+ stats->sock_callstats.read.bins[3],
+ stats->sock_callstats.read.bins[4],
+ stats->sock_callstats.read.bins[5],
+ stats->sock_callstats.read.bins[6],
+ stats->sock_callstats.read.bins[7]);
+}
+
+void tcp_output_write_enhanced_csv (struct TransferInfo *stats) {
+ char timestr[80];
+ iperf_formattime(timestr, sizeof(timestr), (!stats->final ? stats->ts.nextTime : stats->ts.packetTime), \
+ isEnhanced(stats->common), isUTC(stats->common),
+ isEnhanced(stats->common) ? CSVTZ : CSV);
+ intmax_t speed = (intmax_t) (((stats->cntBytes > 0) && (stats->ts.iEnd - stats->ts.iStart) > 0.0) ? \
+ (((double)stats->cntBytes * 8.0) / (stats->ts.iEnd - stats->ts.iStart)) : 0);
+#if !(HAVE_TCP_STATS)
+ printf(reportCSV_bw_write_enhanced_format,
+ timestr,
+ stats->csv_peer,
+ stats->common->transferID,
+ stats->ts.iStart,
+ stats->ts.iEnd,
+ stats->cntBytes,
+ speed,
+ -1,
+ -1,
+ -1,
+ -1,
+ 0,
+ 0);
+#else
+ if (stats->common->transferID == -1) {
+ /* Sums */
+ printf(reportCSV_bw_write_enhanced_format,
+ timestr,
+ stats->csv_peer,
+ stats->common->transferID,
+ stats->ts.iStart,
+ stats->ts.iEnd,
+ stats->cntBytes,
+ speed,
+ stats->sock_callstats.write.WriteCnt,
+ stats->sock_callstats.write.WriteErr,
+ stats->sock_callstats.write.tcpstats.retry,
+ -1,
+ 0,
+ 0);
+ } else if (stats->sock_callstats.write.tcpstats.cwnd > 0) {
+ printf(reportCSV_bw_write_enhanced_format,
+ timestr,
+ stats->csv_peer,
+ stats->common->transferID,
+ stats->ts.iStart,
+ stats->ts.iEnd,
+ stats->cntBytes,
+ speed,
+ stats->sock_callstats.write.WriteCnt,
+ stats->sock_callstats.write.WriteErr,
+ stats->sock_callstats.write.tcpstats.retry,
+ stats->sock_callstats.write.tcpstats.cwnd,
+ stats->sock_callstats.write.tcpstats.rtt,
+ stats->sock_callstats.write.tcpstats.rttvar);
+ } else {
+ printf(reportCSV_bw_write_enhanced_format,
+ timestr,
+ stats->csv_peer,
+ stats->common->transferID,
+ stats->ts.iStart,
+ stats->ts.iEnd,
+ stats->cntBytes,
+ speed,
+ stats->sock_callstats.write.WriteCnt,
+ stats->sock_callstats.write.WriteErr,
+ stats->sock_callstats.write.tcpstats.retry,
+ -1,
+ stats->sock_callstats.write.tcpstats.rtt,
+ 0);
+ }
+#endif
+}
+
+/*
+ * Report the client or listener Settings in default style
+ */
+static void output_window_size (struct ReportSettings *report) {
+ int winsize = getsock_tcp_windowsize(report->common->socket, (report->common->ThreadMode != kMode_Client ? 0 : 1));
+ byte_snprintf(outbuffer, sizeof(outbuffer), winsize, \
+ ((toupper(report->common->Format) == 'B') ? 'B' : 'A'));
+ outbuffer[(sizeof(outbuffer)-1)] = '\0';
+ printf("%s: %s", (isUDP(report->common) ? udp_buffer_size : tcp_window_size), outbuffer);
+ if (report->common->winsize_requested == 0) {
+ printf(" %s", window_default);
+ } else if (winsize != report->common->winsize_requested) {
+ byte_snprintf(outbuffer, sizeof(outbuffer), report->common->winsize_requested,
+ toupper((int)report->common->Format));
+ outbuffer[(sizeof(outbuffer)-1)] = '\0';
+ printf(warn_window_requested, outbuffer);
+ }
+ fflush(stdout);
+}
+static void reporter_output_listener_settings (struct ReportSettings *report) {
+ if (report->common->PortLast > report->common->Port) {
+ printf(server_pid_portrange, (isUDP(report->common) ? "UDP" : "TCP"), \
+ report->common->Port, report->common->PortLast, report->pid);
+ } else {
+ printf(isEnhanced(report->common) ? server_pid_port : server_port,
+ (isUDP(report->common) ? "UDP" : "TCP"), report->common->Port, report->pid);
+ }
+ if (report->common->Localhost != NULL) {
+ if (isEnhanced(report->common) && !SockAddr_isMulticast(&report->local)) {
+ if (report->common->Ifrname) {
+ printf(bind_address_iface, report->common->Localhost, report->common->Ifrname);
+ } else {
+ char *host_ip = (char *) malloc(REPORT_ADDRLEN);
+ if (host_ip != NULL) {
+ if (((struct sockaddr*)(&report->common->local))->sa_family == AF_INET) {
+ if (isHideIPs(report->common)) {
+ inet_ntop_hide(AF_INET, &((struct sockaddr_in*)(&report->common->local))->sin_addr,
+ host_ip, REPORT_ADDRLEN);
+ } else {
+ inet_ntop(AF_INET, &((struct sockaddr_in*)(&report->common->local))->sin_addr,
+ host_ip, REPORT_ADDRLEN);
+ }
+ }
+#if HAVE_IPV6
+ else {
+ inet_ntop(AF_INET6, &((struct sockaddr_in6*)(&report->common->local))->sin6_addr,
+ host_ip, REPORT_ADDRLEN);
+ }
+#endif
+ printf(bind_address, host_ip);
+ free(host_ip);
+ }
+ }
+ }
+ if (SockAddr_isMulticast(&report->local)) {
+ if(!report->common->SSMMulticastStr)
+ if (!report->common->Ifrname)
+ printf(join_multicast, report->common->Localhost);
+ else
+ printf(join_multicast_starg_dev, report->common->Localhost,report->common->Ifrname);
+ else if(!report->common->Ifrname)
+ printf(join_multicast_sg, report->common->SSMMulticastStr, report->common->Localhost);
+ else
+ printf(join_multicast_sg_dev, report->common->SSMMulticastStr, report->common->Localhost, report->common->Ifrname);
+ }
+ }
+ if (isTunDev(report->common) || isTapDev(report->common)) {
+ printf(bind_address_iface_taptun, report->common->Ifrname);
+ }
+ if (isEnhanced(report->common)) {
+ byte_snprintf(outbuffer, sizeof(outbuffer), report->common->BufLen, toupper((int)report->common->Format));
+ byte_snprintf(outbufferext, sizeof(outbufferext), report->common->BufLen / 8, 'A');
+ outbuffer[(sizeof(outbuffer)-1)] = '\0';
+ outbufferext[(sizeof(outbufferext)-1)] = '\0';
+ printf("%s: %s (Dist bin width=%s)\n", server_read_size, outbuffer, outbufferext);
+ }
+ if (isCongestionControl(report->common) && report->common->Congestion) {
+ fprintf(stdout, "TCP congestion control set to %s\n", report->common->Congestion);
+ }
+ if (isOverrideTOS(report->common)) {
+ fprintf(stdout, "Reflected TOS will be set to 0x%x\n", report->common->RTOS);
+ }
+ if (isPrintMSS(report->common)) {
+ if (isTCPMSS(report->common)) {
+ printf(report_mss, report->sockmaxseg);
+ } else {
+ printf(report_default_mss, report->sockmaxseg);
+ }
+ }
+ if (report->common->TOS) {
+ fprintf(stdout, "TOS will be set to 0x%x\n", report->common->TOS);
+ }
+ if (isUDP(report->common)) {
+ if (isSingleClient(report->common)) {
+ fprintf(stdout, "WARN: Suggested to use lower case -u instead of -U (to avoid serialize & bypass of reporter thread)\n");
+ } else if (isSingleClient(report->common)) {
+ fprintf(stdout, "Server set to single client traffic mode per -U (serialize traffic tests)\n");
+ }
+ } else if (isSingleClient(report->common)) {
+ fprintf(stdout, "Server set to single client traffic mode (serialize traffic tests)\n");
+ }
+ if (isMulticast(report->common)) {
+ fprintf(stdout, "Server set to single client traffic mode (per multicast receive)\n");
+ }
+ if (isHistogram(report->common)) {
+ fprintf(stdout, "Enabled receive histograms bin-width=%0.3f ms, bins=%d (clients should use --trip-times)\n", \
+ ((1e3 * report->common->HistBinsize) / pow(10,report->common->HistUnits)), report->common->HistBins);
+ }
+ if (isJitterHistogram(report->common)) {
+ fprintf(stdout, "Enabled jitter histograms (bin-width=%d us)\n", report->common->jitter_binwidth);
+ }
+ if (isFrameInterval(report->common)) {
+#if HAVE_FASTSAMPLING
+ fprintf(stdout, "Frame or burst interval reporting (feature is experimental)\n");
+#else
+ fprintf(stdout, "Frame or burst interval reporting (feature is experimental, ./configure --enable-fastsampling suggested)\n");
+#endif
+ }
+ output_window_size(report);
+ printf("\n");
+ if (isPermitKey(report->common) && report->common->PermitKey) {
+ if (report->common->ListenerTimeout > 0) {
+ fprintf(stdout, "Permit key is '%s' (timeout in %0.1f seconds)\n", report->common->PermitKey, report->common->ListenerTimeout);
+ } else {
+ fprintf(stdout, "Permit key is '%s' (WARN: no timeout)\n", report->common->PermitKey);
+ }
+ }
+ fflush(stdout);
+}
+static void reporter_output_client_settings (struct ReportSettings *report) {
+ char *hoststr = (isHideIPs(report->common) ? report->common->HideHost \
+ : report->common->Host);
+ if (!report->common->Ifrnametx) {
+ printf(isEnhanced(report->common) ? client_pid_port : client_port, hoststr,
+ (isUDP(report->common) ? "UDP" : "TCP"), report->common->Port, report->pid, \
+ (!report->common->threads ? 1 : report->common->threads));
+ } else {
+ printf(client_pid_port_dev, hoststr,
+ (isUDP(report->common) ? "UDP" : "TCP"), report->common->Port, report->pid, \
+ report->common->Ifrnametx, (!report->common->threads ? 1 : report->common->threads));
+ }
+ if ((isEnhanced(report->common) || isNearCongest(report->common)) && !isUDP(report->common)) {
+ byte_snprintf(outbuffer, sizeof(outbuffer), report->common->BufLen, 'B');
+ outbuffer[(sizeof(outbuffer)-1)] = '\0';
+ if (isTcpWriteTimes(report->common)) {
+ printf("%s: %s (writetimer-enabled)\n", client_write_size, outbuffer);
+ } else {
+ printf("%s: %s\n", client_write_size, outbuffer);
+ }
+ }
+ if (isIsochronous(report->common)) {
+ char meanbuf[40];
+ char variancebuf[40];
+ byte_snprintf(meanbuf, sizeof(meanbuf), report->isochstats.mMean, 'a');
+ byte_snprintf(variancebuf, sizeof(variancebuf), report->isochstats.mVariance, 'a');
+ meanbuf[39]='\0'; variancebuf[39]='\0';
+ printf(client_isochronous, report->isochstats.mFPS, meanbuf, variancebuf, (report->isochstats.mBurstInterval/1000.0), (report->isochstats.mBurstIPG/1000.0));
+ }
+ if (isPeriodicBurst(report->common)) {
+ char tmpbuf[40];
+ byte_snprintf(tmpbuf, sizeof(tmpbuf), report->common->BurstSize, 'A');
+ tmpbuf[39]='\0';
+ if (report->common->bbcount) {
+ printf(client_burstperiodcount, tmpbuf, report->common->bbcount, (1.0 / report->common->FPS));
+ } else {
+ printf(client_burstperiod, tmpbuf, (1.0 / report->common->FPS));
+ }
+ }
+ if (isBounceBack(report->common)) {
+ char tmpbuf[40];
+ byte_snprintf(tmpbuf, sizeof(tmpbuf), report->common->bbsize, 'A');
+ tmpbuf[39]='\0';
+ if (isTcpQuickAck(report->common)) {
+ printf(client_bounceback, tmpbuf, report->common->bbhold);
+ } else {
+ printf(client_bounceback_noqack, tmpbuf, report->common->bbhold);
+ }
+ }
+ if (isFQPacing(report->common)) {
+ byte_snprintf(outbuffer, sizeof(outbuffer), report->common->FQPacingRate, 'a');
+ outbuffer[(sizeof(outbuffer)-1)] = '\0';
+ printf(client_fq_pacing,outbuffer);
+ }
+ if (isPrintMSS(report->common)) {
+ if (isTCPMSS(report->common)) {
+ printf(report_mss, report->sockmaxseg);
+ } else {
+ printf(report_default_mss, report->sockmaxseg);
+ }
+ }
+
+ if (isCongestionControl(report->common) && report->common->Congestion) {
+ fprintf(stdout, "TCP congestion control set to %s\n", report->common->Congestion);
+ }
+ if (isEnhanced(report->common)) {
+ if (isNoDelay(report->common)) {
+ fprintf(stdout, "TOS set to 0x%x and nodelay (Nagle off)\n", report->common->TOS);
+ } else {
+ fprintf(stdout, "TOS set to 0x%x (Nagle on)\n", report->common->TOS);
+ }
+ }
+ if (isNearCongest(report->common)) {
+ if (report->common->rtt_weight == NEARCONGEST_DEFAULT) {
+ fprintf(stdout, "TCP near-congestion delay weight set to %2.4f (use --near-congestion=<value> to change)\n", report->common->rtt_weight);
+ } else {
+ fprintf(stdout, "TCP near-congestion delay weight set to %2.4f\n", report->common->rtt_weight);
+ }
+ }
+ if (isSingleClient(report->common)) {
+ fprintf(stdout, "WARN: Client set to bypass reporter thread per -U (suggest use lower case -u instead)\n");
+ }
+ if ((isIPG(report->common) || isUDP(report->common)) && !isIsochronous(report->common)) {
+ byte_snprintf(outbuffer, sizeof(outbuffer), report->common->pktIPG, 'a');
+ outbuffer[(sizeof(outbuffer)-1)] = '\0';
+#ifdef HAVE_KALMAN
+ printf(client_datagram_size_kalman, report->common->BufLen, report->common->pktIPG);
+#else
+ printf(client_datagram_size, report->common->BufLen, report->common->pktIPG);
+#endif
+ }
+ if (isConnectOnly(report->common)) {
+ fprintf(stdout, "TCP three-way-handshake (3WHS) only\n");
+ } else {
+ output_window_size(report);
+ printf("\n");
+#if HAVE_DECL_TCP_NOTSENT_LOWAT
+ if (isWritePrefetch(report->common)) {
+ fprintf(stdout, "Event based writes (pending queue watermark at %d bytes)\n", report->common->WritePrefetch);
+ }
+#endif
+ if (isHistogram(report->common)) {
+ if (!isBounceBack(report->common)) {
+ fprintf(stdout, "Enabled write histograms bin-width=%0.3f ms, bins=%d\n", \
+ ((1e3 * report->common->HistBinsize) / pow(10,report->common->HistUnits)), report->common->HistBins);
+ } else {
+ fprintf(stdout, "Set bounceback histograms to bin-width=%0.3f ms, bins=%d\n", \
+ ((1e3 * report->common->HistBinsize) / pow(10,report->common->HistUnits)), report->common->HistBins);
+ }
+ }
+ }
+ fflush(stdout);
+}
+
+void reporter_connect_printf_tcp_final (struct ConnectionInfo * report) {
+ if (report->connect_times.cnt > 1) {
+ double variance = (report->connect_times.cnt < 2) ? 0 : 1e3* (sqrt(report->connect_times.m2 / (report->connect_times.cnt - 1)));
+ fprintf(stdout, "[ CT] final connect times (min/avg/max/stdev) = %0.3f/%0.3f/%0.3f/%0.3f ms (tot/err) = %" PRIdMAX "/%" PRIdMAX "\n", \
+ report->connect_times.min, \
+ (report->connect_times.sum / report->connect_times.cnt), \
+ report->connect_times.max, variance, \
+ (report->connect_times.cnt + report->connect_times.err), \
+ report->connect_times.err);
+ }
+ fflush(stdout);
+}
+
+void reporter_print_connection_report (struct ConnectionInfo *report) {
+ assert(report->common);
+ // copy the inet_ntop into temp buffers, to avoid overwriting
+ char local_addr[REPORT_ADDRLEN];
+ char remote_addr[REPORT_ADDRLEN];
+ struct sockaddr *local = ((struct sockaddr*)&report->common->local);
+ struct sockaddr *peer = ((struct sockaddr*)&report->common->peer);
+ outbuffer[0]='\0';
+ outbufferext[0]='\0';
+ char *b = &outbuffer[0];
+
+#if HAVE_DECL_TCP_WINDOW_CLAMP
+ if (!isUDP(report->common) && isRxClamp(report->common)) {
+ snprintf(b, SNBUFFERSIZE-strlen(b), " (%s%d)", "clamp=", report->common->ClampSize);
+ b += strlen(b);
+ }
+#endif
+#if HAVE_DECL_TCP_NOTSENT_LOWAT
+ if (!isUDP(report->common) && (report->common->socket > 0) && isWritePrefetch(report->common)) {
+ snprintf(b, SNBUFFERSIZE-strlen(b), " (%s%d)", "prefetch=", report->common->WritePrefetch);
+ b += strlen(b);
+ }
+#endif
+ if (isIsochronous(report->common)) {
+ snprintf(b, SNBUFFERSIZE-strlen(b), " (isoch)");
+ b += strlen(b);
+ }
+ if (isPeriodicBurst(report->common) && (report->common->ThreadMode != kMode_Client) && !isServerReverse(report->common)) {
+#if HAVE_FASTSAMPLING
+ snprintf(b, SNBUFFERSIZE-strlen(b), " (burst-period=%0.4fs)", (1.0 / report->common->FPS));
+#else
+ snprintf(b, SNBUFFERSIZE-strlen(b), " (burst-period=%0.2fs)", (1.0 / report->common->FPS));
+#endif
+ b += strlen(b);
+ }
+ if (isFullDuplex(report->common)) {
+ snprintf(b, SNBUFFERSIZE-strlen(b), " (full-duplex)");
+ b += strlen(b);
+ } else if (isServerReverse(report->common) || isReverse(report->common)) {
+ snprintf(b, SNBUFFERSIZE-strlen(b), " (reverse)");
+ b += strlen(b);
+ if (isFQPacing(report->common)) {
+ snprintf(b, SNBUFFERSIZE-strlen(b), " (fq)");
+ b += strlen(b);
+ }
+ }
+ if (isTxStartTime(report->common)) {
+ snprintf(b, SNBUFFERSIZE-strlen(b), " (epoch-start)");
+ b += strlen(b);
+ }
+ if (isBounceBack(report->common)) {
+ if (isTcpQuickAck(report->common)) {
+ snprintf(b, SNBUFFERSIZE-strlen(b), " (bb w/quickack len/hold=%d/%d)", report->common->bbsize, report->common->bbhold);
+ } else {
+ snprintf(b, SNBUFFERSIZE-strlen(b), " (bb len/hold=%d/%d)", report->common->bbsize, report->common->bbhold);
+ }
+ b += strlen(b);
+ }
+ if (isL2LengthCheck(report->common)) {
+ snprintf(b, SNBUFFERSIZE-strlen(b), " (l2mode)");
+ b += strlen(b);
+ }
+ if (isUDP(report->common) && isNoUDPfin(report->common)) {
+ snprintf(b, SNBUFFERSIZE-strlen(b), " (no-udp-fin)");
+ b += strlen(b);
+ }
+ if (isTripTime(report->common)) {
+ snprintf(b, SNBUFFERSIZE-strlen(b), " (trip-times)");
+ b += strlen(b);
+ }
+ if (isEnhanced(report->common)) {
+ snprintf(b, SNBUFFERSIZE-strlen(b), " (sock=%d)", report->common->socket);;
+ b += strlen(b);
+ }
+ if (isOverrideTOS(report->common)) {
+ if (isFullDuplex(report->common)) {
+ snprintf(b, SNBUFFERSIZE-strlen(b), " (tos rx/tx=0x%x/0x%x)", report->common->TOS, report->common->RTOS);
+ } else if (isReverse(report->common)) {
+ snprintf(b, SNBUFFERSIZE-strlen(b), " (tos tx=0x%x)", report->common->TOS);
+ }
+ b += strlen(b);
+ } else if (report->common->TOS) {
+ if (isFullDuplex(report->common) || isBounceBack(report->common)) {
+ snprintf(b, SNBUFFERSIZE-strlen(b), " (tos rx/tx=0x%x/0x%x)", report->common->TOS, report->common->TOS);
+ } else if (isReverse(report->common)) {
+ snprintf(b, SNBUFFERSIZE-strlen(b), " (tos tx=0x%x)", report->common->TOS);
+ } else {
+ snprintf(b, SNBUFFERSIZE-strlen(b), " (tos rx=0x%x)", report->common->TOS);
+ }
+ b += strlen(b);
+ }
+ if (isEnhanced(report->common) || isPeerVerDetect(report->common)) {
+ if (report->peerversion[0] != '\0') {
+ snprintf(b, SNBUFFERSIZE-strlen(b), "%s", report->peerversion);
+ b += strlen(b);
+ }
+ }
+#if HAVE_DECL_TCP_QUICKACK
+ if (isTcpQuickAck(report->common) && !isBounceBack(report->common)) {
+ snprintf(b, SNBUFFERSIZE-strlen(b), " (qack)");
+ b += strlen(b);
+ }
+#endif
+#if HAVE_TCP_STATS
+ if (!isUDP(report->common) && (report->tcpinitstats.isValid)) {
+ snprintf(b, SNBUFFERSIZE-strlen(b), " (icwnd/mss/irtt=%u/%u/%u)", \
+ report->tcpinitstats.cwnd, report->tcpinitstats.mss_negotiated, report->tcpinitstats.rtt);
+ b += strlen(b);
+ }
+#endif
+
+ if ((isFullDuplex(report->common) || !isServerReverse(report->common)) \
+ && (isEnhanced(report->common) || isConnectOnly(report->common))) {
+ if (report->connect_timestamp.tv_sec > 0) {
+ char timestr[80];
+ iperf_formattime(timestr, sizeof(timestr), report->connect_timestamp, \
+ isEnhanced(report->common), isUTC(report->common), YearThruSecTZ);
+ if (!isUDP(report->common) && (report->common->ThreadMode == kMode_Client) && (report->tcpinitstats.connecttime > 0)) {
+ snprintf(b, SNBUFFERSIZE-strlen(b), " (ct=%4.2f ms) on %s", report->tcpinitstats.connecttime, timestr);
+ } else {
+ snprintf(b, SNBUFFERSIZE-strlen(b), " on %s", timestr);
+ }
+ b += strlen(b);
+ }
+ }
+ if (local->sa_family == AF_INET) {
+ if (isHideIPs(report->common)) {
+ inet_ntop_hide(AF_INET, &((struct sockaddr_in*)local)->sin_addr, local_addr, REPORT_ADDRLEN);
+ } else {
+ inet_ntop(AF_INET, &((struct sockaddr_in*)local)->sin_addr, local_addr, REPORT_ADDRLEN);
+ }
+ }
+#if HAVE_IPV6
+ else {
+ inet_ntop(AF_INET6, &((struct sockaddr_in6*)local)->sin6_addr, local_addr, REPORT_ADDRLEN);
+ }
+#endif
+ if (peer->sa_family == AF_INET) {
+ if (isHideIPs(report->common)) {
+ inet_ntop_hide(AF_INET, &((struct sockaddr_in*)peer)->sin_addr, remote_addr, REPORT_ADDRLEN);
+ } else {
+ inet_ntop(AF_INET, &((struct sockaddr_in*)peer)->sin_addr, remote_addr, REPORT_ADDRLEN);
+ }
+ }
+#if HAVE_IPV6
+ else {
+ inet_ntop(AF_INET6, &((struct sockaddr_in6*)peer)->sin6_addr, remote_addr, REPORT_ADDRLEN);
+ }
+#endif
+#if HAVE_IPV6
+ if (report->common->KeyCheck) {
+ if (isEnhanced(report->common) && report->common->Ifrname && (strlen(report->common->Ifrname) < SNBUFFERSIZE-strlen(b))) {
+ printf(report_peer_dev, report->common->transferIDStr, local_addr, report->common->Ifrname, \
+ (local->sa_family == AF_INET ? ntohs(((struct sockaddr_in*)local)->sin_port) : \
+ ntohs(((struct sockaddr_in6*)local)->sin6_port)), \
+ remote_addr, (peer->sa_family == AF_INET ? ntohs(((struct sockaddr_in*)peer)->sin_port) : \
+ ntohs(((struct sockaddr_in6*)peer)->sin6_port)), outbuffer);
+ } else {
+ printf(report_peer, report->common->transferIDStr, local_addr, \
+ (local->sa_family == AF_INET ? ntohs(((struct sockaddr_in*)local)->sin_port) : \
+ ntohs(((struct sockaddr_in6*)local)->sin6_port)), \
+ remote_addr, (peer->sa_family == AF_INET ? ntohs(((struct sockaddr_in*)peer)->sin_port) : \
+ ntohs(((struct sockaddr_in6*)peer)->sin6_port)), outbuffer);
+ }
+ } else {
+ printf(report_peer_fail, local_addr, \
+ (local->sa_family == AF_INET ? ntohs(((struct sockaddr_in*)local)->sin_port) : \
+ ntohs(((struct sockaddr_in6*)local)->sin6_port)), \
+ remote_addr, (peer->sa_family == AF_INET ? ntohs(((struct sockaddr_in*)peer)->sin_port) : \
+ ntohs(((struct sockaddr_in6*)peer)->sin6_port)), outbuffer);
+ }
+
+#else
+ if (report->common->KeyCheck) {
+ if (isEnhanced(report->common) && report->common->Ifrname && (strlen(report->common->Ifrname) < SNBUFFERSIZE-strlen(b))) {
+ printf(report_peer_dev, report->common->transferIDStr, local_addr, report->common->Ifrname, \
+ local_addr, (local->sa_family == AF_INET ? ntohs(((struct sockaddr_in*)local)->sin_port) : 0), \
+ remote_addr, (peer->sa_family == AF_INET ? ntohs(((struct sockaddr_in*)peer)->sin_port) : 0), \
+ outbuffer);
+ } else {
+ printf(report_peer, report->common->transferIDStr, \
+ local_addr, (local->sa_family == AF_INET ? ntohs(((struct sockaddr_in*)local)->sin_port) : 0), \
+ remote_addr, (peer->sa_family == AF_INET ? ntohs(((struct sockaddr_in*)peer)->sin_port) : 0), \
+ outbuffer);
+ }
+ } else {
+ printf(report_peer_fail, local_addr, (local->sa_family == AF_INET ? ntohs(((struct sockaddr_in*)local)->sin_port) : 0), \
+ remote_addr, (peer->sa_family == AF_INET ? ntohs(((struct sockaddr_in*)peer)->sin_port) : 0), \
+ outbuffer);
+ }
+#endif
+ if ((report->common->ThreadMode == kMode_Client) && !isServerReverse(report->common)) {
+ if (isTxHoldback(report->common) || isTxStartTime(report->common)) {
+ struct timeval now;
+#ifdef HAVE_CLOCK_GETTIME
+ struct timespec t1;
+ clock_gettime(CLOCK_REALTIME, &t1);
+ now.tv_sec = t1.tv_sec;
+ now.tv_usec = t1.tv_nsec / 1000;
+#else
+ gettimeofday(&now, NULL);
+#endif
+ int seconds_from_now;
+ if (isTxHoldback(report->common)) {
+ seconds_from_now = report->txholdbacktime.tv_sec;
+ if (report->txholdbacktime.tv_usec > 0)
+ seconds_from_now++;
+ } else {
+ seconds_from_now = ceil(TimeDifference(report->epochStartTime, now));
+ }
+ struct timeval start;
+ start = now;
+ char start_timebuf[80];
+ start.tv_sec = now.tv_sec + seconds_from_now;
+ char now_timebuf[80];
+ iperf_formattime(now_timebuf, sizeof(now_timebuf), now, \
+ isEnhanced(report->common), isUTC(report->common), YearThruSecTZ);
+ iperf_formattime(start_timebuf, sizeof(start_timebuf), start, \
+ isEnhanced(report->common), isUTC(report->common), YearThruSec);
+ if (seconds_from_now > 0) {
+ printf(client_report_epoch_start_current, report->common->transferID, seconds_from_now, \
+ start_timebuf, now_timebuf);
+ } else if (!isBounceBack(report->common)) {
+ printf(warn_start_before_now, report->common->transferID, report->epochStartTime.tv_sec, \
+ report->epochStartTime.tv_usec, start_timebuf, now_timebuf);
+ }
+ }
+ }
+ fflush(stdout);
+}
+
+void reporter_print_settings_report (struct ReportSettings *report) {
+ assert(report != NULL);
+ report->pid = (int) getpid();
+ printf("%s", separator_line);
+ if (report->common->ThreadMode == kMode_Listener) {
+ reporter_output_listener_settings(report);
+ } else {
+ reporter_output_client_settings(report);
+ }
+ printf("%s", separator_line);
+ fflush(stdout);
+}
+
+void reporter_peerversion (struct ConnectionInfo *report, uint32_t upper, uint32_t lower) {
+ if (!upper || !lower) {
+ report->peerversion[0]='\0';
+ } else {
+ int rel, major, minor, alpha;
+ rel = (upper & 0xFFFF0000) >> 16;
+ major = (upper & 0x0000FFFF);
+ minor = (lower & 0xFFFF0000) >> 16;
+ alpha = (lower & 0x0000000F);
+ snprintf(report->peerversion, (PEERVERBUFSIZE-10), " (peer %d.%d.%d)", rel, major, minor);
+ switch(alpha) {
+ case 0:
+ sprintf(report->peerversion + strlen(report->peerversion) - 1,"-dev)");
+ break;
+ case 1:
+ sprintf(report->peerversion + strlen(report->peerversion) - 1,"-rc)");
+ break;
+ case 2:
+ sprintf(report->peerversion + strlen(report->peerversion) - 1,"-rc2)");
+ break;
+ case 3:
+ break;
+ case 4:
+ sprintf(report->peerversion + strlen(report->peerversion) - 1,"-private)");
+ break;
+ case 5:
+ sprintf(report->peerversion + strlen(report->peerversion) - 1,"-master)");
+ break;
+ default:
+ sprintf(report->peerversion + strlen(report->peerversion) - 1, "-unk)");
+ }
+ report->peerversion[PEERVERBUFSIZE-1]='\0';
+ }
+}
+
+void reporter_print_server_relay_report (struct ServerRelay *report) {
+ printf(server_reporting, report->info.common->transferID);
+ if (isTripTime(report->info.common) || isEnhanced(report->info.common)) {
+ udp_output_read_triptime(&report->info);
+ } else {
+ udp_output_read(&report->info);
+ }
+ fflush(stdout);
+}
diff --git a/src/Reporter.c b/src/Reporter.c
new file mode 100644
index 0000000..bbd6fd5
--- /dev/null
+++ b/src/Reporter.c
@@ -0,0 +1,1937 @@
+/*---------------------------------------------------------------
+ * Copyright (c) 1999,2000,2001,2002,2003
+ * The Board of Trustees of the University of Illinois
+ * All Rights Reserved.
+ *---------------------------------------------------------------
+ * 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.
+ * ________________________________________________________________
+ * National Laboratory for Applied Network Research
+ * National Center for Supercomputing Applications
+ * University of Illinois at Urbana-Champaign
+ * http://www.ncsa.uiuc.edu
+ * ________________________________________________________________
+ *
+ * Reporter.c
+ * by Kevin Gibbs <kgibbs@nlanr.net>
+ *
+ * Major rewrite by Robert McMahon (Sept 2020, ver 2.0.14)
+ * ________________________________________________________________ */
+
+#include <math.h>
+#include "headers.h"
+#include "Settings.hpp"
+#include "util.h"
+#include "Reporter.h"
+#include "Thread.h"
+#include "Locale.h"
+#include "PerfSocket.hpp"
+#include "SocketAddr.h"
+#include "histogram.h"
+#include "delay.h"
+#include "packet_ring.h"
+#include "payloads.h"
+#include "gettcpinfo.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef INITIAL_PACKETID
+# define INITIAL_PACKETID 0
+#endif
+
+struct ReportHeader *ReportRoot = NULL;
+struct ReportHeader *ReportPendingHead = NULL;
+struct ReportHeader *ReportPendingTail = NULL;
+
+// Reporter's reset of stats after a print occurs
+static void reporter_reset_transfer_stats_client_tcp(struct TransferInfo *stats);
+static void reporter_reset_transfer_stats_client_udp(struct TransferInfo *stats);
+static void reporter_reset_transfer_stats_server_udp(struct TransferInfo *stats);
+static void reporter_reset_transfer_stats_server_tcp(struct TransferInfo *stats);
+
+// code for welfornd's algorithm to produce running mean/min/max/var
+static void reporter_update_mmm (struct MeanMinMaxStats *stats, double value);
+static void reporter_reset_mmm (struct MeanMinMaxStats *stats);
+
+
+// one way delay (OWD) calculations
+static void reporter_handle_packet_oneway_transit(struct TransferInfo *stats, struct ReportStruct *packet);
+static void reporter_handle_isoch_oneway_transit_tcp(struct TransferInfo *stats, struct ReportStruct *packet);
+static void reporter_handle_isoch_oneway_transit_udp(struct TransferInfo *stats, struct ReportStruct *packet);
+static void reporter_handle_frame_isoch_oneway_transit(struct TransferInfo *stats, struct ReportStruct *packet);
+static void reporter_handle_txmsg_oneway_transit(struct TransferInfo *stats, struct ReportStruct *packet);
+static void reporter_handle_rxmsg_oneway_transit(struct TransferInfo *stats, struct ReportStruct *packet);
+
+static inline void reporter_compute_packet_pps (struct TransferInfo *stats, struct ReportStruct *packet);
+
+#if HAVE_TCP_STATS
+static inline void reporter_handle_packet_tcpistats(struct ReporterData *data, struct ReportStruct *packet);
+#endif
+static struct ConnectionInfo *myConnectionReport;
+
+void PostReport (struct ReportHeader *reporthdr) {
+#ifdef HAVE_THREAD_DEBUG
+ char rs[REPORTTXTMAX];
+ reporttype_text(reporthdr, &rs[0]);
+ thread_debug("Jobq *POST* report %p (%s)", reporthdr, &rs[0]);
+#endif
+ if (reporthdr) {
+#ifdef HAVE_THREAD
+ /*
+ * Update the ReportRoot to include this report.
+ */
+ Condition_Lock(ReportCond);
+ reporthdr->next = NULL;
+ if (!ReportPendingHead) {
+ ReportPendingHead = reporthdr;
+ ReportPendingTail = reporthdr;
+ } else {
+ ReportPendingTail->next = reporthdr;
+ ReportPendingTail = reporthdr;
+ }
+ Condition_Unlock(ReportCond);
+ // wake up the reporter thread
+ Condition_Signal(&ReportCond);
+#else
+ /*
+ * Process the report in this thread
+ */
+ reporthdr->next = NULL;
+ reporter_process_report(reporthdr);
+#endif
+ }
+}
+/*
+ * ReportPacket is called by a transfer agent to record
+ * the arrival or departure of a "packet" (for TCP it
+ * will actually represent many packets). This needs to
+ * be as simple and fast as possible as it gets called for
+ * every "packet".
+ *
+ * Returns true when the tcpinfo was sampled, false ohterwise
+ */
+bool ReportPacket (struct ReporterData* data, struct ReportStruct *packet) {
+ assert(data != NULL);
+
+ bool rc = false;
+ #ifdef HAVE_THREAD_DEBUG
+ if (packet->packetID < 0) {
+ thread_debug("Reporting last packet for %p qdepth=%d sock=%d", (void *) data, packetring_getcount(data->packetring), data->info.common->socket);
+ }
+ #endif
+#if HAVE_TCP_STATS
+ struct TransferInfo *stats = &data->info;
+ packet->tcpstats.isValid = false;
+ if (stats->isEnableTcpInfo) {
+ if (!TimeZero(stats->ts.nextTCPSampleTime) && (TimeDifference(stats->ts.nextTCPSampleTime, packet->packetTime) < 0)) {
+ gettcpinfo(data->info.common->socket, &packet->tcpstats);
+ TimeAdd(stats->ts.nextTCPSampleTime, stats->ts.intervalTime);
+ } else {
+ gettcpinfo(data->info.common->socket, &packet->tcpstats);
+ }
+ }
+#endif
+
+ // Note for threaded operation all that needs
+ // to be done is to enqueue the packet data
+ // into the ring.
+ packetring_enqueue(data->packetring, packet);
+ // The traffic thread calls the reporting process
+ // directly forr non-threaded operation
+ // These defeats the puropse of separating
+ // traffic i/o from user i/o and really
+ // should be avoided.
+ #ifdef HAVE_THREAD
+ // bypass the reporter thread here for single UDP
+ if (isSingleUDP(data->info.common))
+ reporter_process_transfer_report(data);
+ #else
+ /*
+ * Process the report in this thread
+ */
+ reporter_process_transfer_report(data);
+ #endif
+
+ return rc;
+}
+
+/*
+ * EndJob is called by a traffic thread to inform the reporter
+ * thread to print a final report and to remove the data report from its jobq.
+ * It also handles the freeing reports and other closing actions
+ */
+int EndJob (struct ReportHeader *reporthdr, struct ReportStruct *finalpacket) {
+ assert(reporthdr!=NULL);
+ assert(finalpacket!=NULL);
+ struct ReporterData *report = (struct ReporterData *) reporthdr->this_report;
+ struct ReportStruct packet;
+
+ memset(&packet, 0, sizeof(struct ReportStruct));
+ int do_close = 1;
+ /*
+ * Using PacketID of -1 ends reporting
+ * It pushes a "special packet" through
+ * the packet ring which will be detected
+ * by the reporter thread as and end of traffic
+ * event
+ */
+#if HAVE_TCP_STATS
+ // tcpi stats are sampled on a final packet
+ struct TransferInfo *stats = &report->info;
+ if (stats->isEnableTcpInfo) {
+ gettcpinfo(report->info.common->socket, &finalpacket->tcpstats);
+ }
+#endif
+ // clear the reporter done predicate
+ report->packetring->consumerdone = 0;
+ // the negative packetID is used to inform the report thread this traffic thread is done
+ packet.packetID = -1;
+ packet.packetLen = finalpacket->packetLen;
+ packet.packetTime = finalpacket->packetTime;
+ if (isSingleUDP(report->info.common)) {
+ packetring_enqueue(report->packetring, &packet);
+ reporter_process_transfer_report(report);
+ } else {
+ ReportPacket(report, &packet);
+#ifdef HAVE_THREAD_DEBUG
+ thread_debug( "Traffic thread awaiting reporter to be done with %p and cond %p", (void *)report, (void *) report->packetring->awake_producer);
+#endif
+ Condition_Lock((*(report->packetring->awake_producer)));
+ while (!report->packetring->consumerdone) {
+ // This wait time is the lag between the reporter thread
+ // and the traffic thread, a reporter thread with lots of
+ // reports (e.g. fastsampling) can lag per the i/o
+ Condition_TimedWait(report->packetring->awake_producer, 1);
+ // printf("Consumer done may be stuck\n");
+ }
+ Condition_Unlock((*(report->packetring->awake_producer)));
+ }
+ if (report->FullDuplexReport && isFullDuplex(report->FullDuplexReport->info.common)) {
+ if (fullduplex_stop_barrier(&report->FullDuplexReport->fullduplex_barrier)) {
+ struct Condition *tmp = &report->FullDuplexReport->fullduplex_barrier.await;
+ Condition_Destroy(tmp);
+#if HAVE_THREAD_DEBUG
+ thread_debug("Socket fullduplex close sock=%d", report->FullDuplexReport->info.common->socket);
+#endif
+ FreeSumReport(report->FullDuplexReport);
+ } else {
+ do_close = 0;
+ }
+ }
+ return do_close;
+}
+
+// This is used to determine the packet/cpu load into the reporter thread
+// If the overall reporter load is too low, add some yield
+// or delay so the traffic threads can fill the packet rings
+#define MINPACKETDEPTH 10
+#define MINPERQUEUEDEPTH 20
+#define REPORTERDELAY_DURATION 16000 // units is microseconds
+struct ConsumptionDetectorType {
+ int accounted_packets;
+ int accounted_packet_threads;
+ int reporter_thread_suspends ;
+};
+struct ConsumptionDetectorType consumption_detector = \
+ {.accounted_packets = 0, .accounted_packet_threads = 0, .reporter_thread_suspends = 0};
+
+static inline void reset_consumption_detector (void) {
+ consumption_detector.accounted_packet_threads = thread_numtrafficthreads();
+ if ((consumption_detector.accounted_packets = thread_numtrafficthreads() * MINPERQUEUEDEPTH) <= MINPACKETDEPTH) {
+ consumption_detector.accounted_packets = MINPACKETDEPTH;
+ }
+}
+static inline void apply_consumption_detector (void) {
+ if (--consumption_detector.accounted_packet_threads <= 0) {
+ // All active threads have been processed for the loop,
+ // reset the thread counter and check the consumption rate
+ // If the rate is too low add some delay to the reporter
+ consumption_detector.accounted_packet_threads = thread_numtrafficthreads();
+ // Check to see if we need to suspend the reporter
+ if (consumption_detector.accounted_packets > 0) {
+ /*
+ * Suspend the reporter thread for some (e.g. 4) milliseconds
+ *
+ * This allows the thread to receive client or server threads'
+ * packet events in "aggregates." This can reduce context
+ * switching allowing for better CPU utilization,
+ * which is very noticble on CPU constrained systems.
+ */
+ delay_loop(REPORTERDELAY_DURATION);
+ consumption_detector.reporter_thread_suspends++;
+ // printf("DEBUG: forced reporter suspend, accounted=%d, queueue depth after = %d\n", accounted_packets, getcount_packetring(reporthdr));
+ } else {
+ // printf("DEBUG: no suspend, accounted=%d, queueue depth after = %d\n", accounted_packets, getcount_packetring(reporthdr));
+ }
+ reset_consumption_detector();
+ }
+}
+
+#ifdef HAVE_THREAD_DEBUG
+static void reporter_jobq_dump(void) {
+ thread_debug("reporter thread job queue request lock");
+ Condition_Lock(ReportCond);
+ struct ReportHeader *itr = ReportRoot;
+ while (itr) {
+ thread_debug("Job in queue %p",(void *) itr);
+ itr = itr->next;
+ }
+ Condition_Unlock(ReportCond);
+ thread_debug("reporter thread job queue unlock");
+}
+#endif
+
+
+/* Concatenate pending reports and return the head */
+static inline struct ReportHeader *reporter_jobq_set_root (struct thread_Settings *inSettings) {
+ struct ReportHeader *root = NULL;
+ Condition_Lock(ReportCond);
+ // check the jobq for empty
+ if (ReportRoot == NULL) {
+ sInterupted = 0; // reset flags in reporter thread emtpy context
+ // The reporter is starting from an empty state
+ // so set the load detect to trigger an initial delay
+ if (!isSingleUDP(inSettings)) {
+ reset_consumption_detector();
+ reporter_default_heading_flags((inSettings->mReportMode == kReport_CSV));
+ }
+ // Only hang the timed wait if more than this thread is active
+ if (!ReportPendingHead && (thread_numuserthreads() > 1)) {
+ Condition_TimedWait(&ReportCond, 1);
+#ifdef HAVE_THREAD_DEBUG
+ thread_debug( "Jobq *WAIT* exit %p/%p cond=%p threads u/t=%d/%d", \
+ (void *) ReportRoot, (void *) ReportPendingHead, \
+ (void *) &ReportCond, thread_numuserthreads(), thread_numtrafficthreads());
+#endif
+ }
+ }
+ // update the jobq per pending reports
+ if (ReportPendingHead) {
+ ReportPendingTail->next = ReportRoot;
+ ReportRoot = ReportPendingHead;
+#ifdef HAVE_THREAD_DEBUG
+ thread_debug( "Jobq *ROOT* %p (last=%p)", \
+ (void *) ReportRoot, (void * ) ReportPendingTail->next);
+#endif
+ ReportPendingHead = NULL;
+ ReportPendingTail = NULL;
+ }
+ root = ReportRoot;
+ Condition_Unlock(ReportCond);
+ return root;
+}
+/*
+ * Welford's online algorithm
+ *
+ * # For a new value newValue, compute the new count, new mean, the new M2.
+ * # mean accumulates the mean of the entire dataset
+ * # M2 aggregates the squared distance from the mean
+ * # count aggregates the number of samples seen so far
+ * def update(existingAggregate, newValue):
+ * (count, mean, M2) = existingAggregate
+ * count += 1
+ * delta = newValue - mean
+ * mean += delta / count
+ * delta2 = newValue - mean
+ * M2 += delta * delta2
+ * return (count, mean, M2)
+ *
+ * # Retrieve the mean, variance and sample variance from an aggregate
+ * def finalize(existingAggregate):
+ * (count, mean, M2) = existingAggregate
+ * if count < 2:
+ * return float("nan")
+ * else:
+ * (mean, variance, sampleVariance) = (mean, M2 / count, M2 / (count - 1))
+ * return (mean, variance, sampleVariance)
+ *
+ */
+static void reporter_update_mmm (struct MeanMinMaxStats *stats, double value) {
+ assert(stats != NULL);
+ stats->cnt++;
+ if (stats->cnt == 1) {
+ // Very first entry
+ stats->min = value;
+ stats->max = value;
+ stats->sum = value;
+ stats->vd = value;
+ stats->mean = value;
+ stats->m2 = 0;
+ stats->sum = value;
+ } else {
+ stats->sum += value;
+ stats->vd = value - stats->mean;
+ stats->mean += (stats->vd / stats->cnt);
+ stats->m2 += stats->vd * (value - stats->mean);
+ // mean min max tests
+ if (value < stats->min)
+ stats->min = value;
+ if (value > stats->max)
+ stats->max = value;
+ }
+ // fprintf(stderr,"**** mmm(%d) val/sum=%f/%f mmm=%f/%f/%f/%f\n", stats->cnt, value, stats->sum, stats->mean, stats->min, stats->max, stats->m2);
+}
+static void reporter_reset_mmm (struct MeanMinMaxStats *stats) {
+ stats->min = FLT_MAX;
+ stats->max = FLT_MIN;
+ stats->sum = 0;
+ stats->vd = 0;
+ stats->mean = 0;
+ stats->m2 = 0;
+ stats->cnt = 0;
+};
+
+/*
+ * This function is the loop that the reporter thread processes
+ */
+void reporter_spawn (struct thread_Settings *thread) {
+#ifdef HAVE_THREAD_DEBUG
+ thread_debug( "Reporter thread started");
+#endif
+ if (isEnhanced(thread)) {
+ myConnectionReport = InitConnectOnlyReport(thread);
+ }
+ /*
+ * reporter main loop needs to wait on all threads being started
+ */
+ Condition_Lock(threads_start.await);
+ while (!threads_start.ready) {
+ Condition_TimedWait(&threads_start.await, 1);
+ }
+ Condition_Unlock(threads_start.await);
+#ifdef HAVE_THREAD_DEBUG
+ thread_debug( "Reporter await done");
+#endif
+
+ //
+ // Signal to other (client) threads that the
+ // reporter is now running.
+ //
+ Condition_Lock(reporter_state.await);
+ reporter_state.ready = 1;
+ Condition_Unlock(reporter_state.await);
+ Condition_Broadcast(&reporter_state.await);
+#if HAVE_SCHED_SETSCHEDULER
+ // set reporter thread to realtime if requested
+ thread_setscheduler(thread);
+#endif
+ /*
+ * Keep the reporter thread alive under the following conditions
+ *
+ * o) There are more reports to output, ReportRoot has a report
+ * o) The number of threads is greater than one which indicates
+ * either traffic threads are still running or a Listener thread
+ * is running. If equal to 1 then only the reporter thread is alive
+ */
+ while ((reporter_jobq_set_root(thread) != NULL) || (thread_numuserthreads() > 1)){
+#ifdef HAVE_THREAD_DEBUG
+ // thread_debug( "Jobq *HEAD* %p (%d)", (void *) ReportRoot, thread_numuserthreads());
+#endif
+ if (ReportRoot) {
+ // https://blog.kloetzl.info/beautiful-code/
+ // Linked list removal/processing is derived from:
+ //
+ // remove_list_entry(entry) {
+ // indirect = &head;
+ // while ((*indirect) != entry) {
+ // indirect = &(*indirect)->next;
+ // }
+ // *indirect = entry->next
+ // }
+ struct ReportHeader **work_item = &ReportRoot;
+ while (*work_item) {
+#ifdef HAVE_THREAD_DEBUG
+ // thread_debug( "Jobq *NEXT* %p", (void *) *work_item);
+#endif
+ // Report process report returns true
+ // when a report needs to be removed
+ // from the jobq. Also, work item might
+ // be removed as part of processing
+ // Store a cached pointer for the linked list maitenance
+ struct ReportHeader *tmp = (*work_item)->next;
+ if (reporter_process_report(*work_item)) {
+#ifdef HAVE_THREAD_DEBUG
+ thread_debug("Jobq *REMOVE* %p", (void *) (*work_item));
+#endif
+ // memory for *work_item is gone by now
+ *work_item = tmp;
+ if (!tmp)
+ break;
+ }
+ work_item = &(*work_item)->next;
+ }
+ }
+ }
+ if (myConnectionReport) {
+ if (myConnectionReport->connect_times.cnt > 1) {
+ reporter_connect_printf_tcp_final(myConnectionReport);
+ }
+ FreeConnectionReport(myConnectionReport);
+ }
+#ifdef HAVE_THREAD_DEBUG
+ if (sInterupted)
+ reporter_jobq_dump();
+ thread_debug("Reporter thread finished user/traffic %d/%d", thread_numuserthreads(), thread_numtrafficthreads());
+#endif
+}
+
+// The Transfer or Data report is by far the most complicated report
+int reporter_process_transfer_report (struct ReporterData *this_ireport) {
+ assert(this_ireport != NULL);
+ struct TransferInfo *sumstats = (this_ireport->GroupSumReport ? &this_ireport->GroupSumReport->info : NULL);
+ struct TransferInfo *fullduplexstats = (this_ireport->FullDuplexReport ? &this_ireport->FullDuplexReport->info : NULL);
+ int need_free = 0;
+ // The consumption detector applies delay to the reporter
+ // thread when its consumption rate is too low. This allows
+ // the traffic threads to send aggregates vs thrash
+ // the packet rings. The dissimilarity between the thread
+ // speeds is due to the performance differences between i/o
+ // bound threads vs cpu bound ones, and it's expected
+ // that reporter thread being CPU limited should be much
+ // faster than the traffic threads, even in aggregate.
+ // Note: If this detection is not going off it means
+ // the system is likely CPU bound and iperf is now likely
+ // becoming a CPU bound test vs a network i/o bound test
+ if (!isSingleUDP(this_ireport->info.common))
+ apply_consumption_detector();
+ // If there are more packets to process then handle them
+ struct ReportStruct *packet = NULL;
+ int advance_jobq = 0;
+ while (!advance_jobq && (packet = packetring_dequeue(this_ireport->packetring))) {
+ // Increment the total packet count processed by this thread
+ // this will be used to make decisions on if the reporter
+ // thread should add some delay to eliminate cpu thread
+ // thrashing,
+ consumption_detector.accounted_packets--;
+ // Check against a final packet event on this packet ring
+#if HAVE_TCP_STATS
+ if (this_ireport->info.isEnableTcpInfo && packet->tcpstats.isValid) {
+ reporter_handle_packet_tcpistats(this_ireport, packet);
+ }
+#endif
+ if (!(packet->packetID < 0)) {
+ // Check to output any interval reports,
+ // bursts need to report the packet first
+ if (this_ireport->packet_handler_pre_report) {
+ (*this_ireport->packet_handler_pre_report)(this_ireport, packet);
+ }
+ if (this_ireport->transfer_interval_handler) {
+ advance_jobq = (*this_ireport->transfer_interval_handler)(this_ireport, packet);
+ }
+ if (this_ireport->packet_handler_post_report) {
+ (*this_ireport->packet_handler_post_report)(this_ireport, packet);
+ }
+ // Sum reports update the report header's last
+ // packet time after the handler. This means
+ // the report header's packet time will be
+ // the previous time before the interval
+ if (sumstats)
+ sumstats->ts.packetTime = packet->packetTime;
+ if (fullduplexstats)
+ fullduplexstats->ts.packetTime = packet->packetTime;
+ } else {
+ need_free = 1;
+ advance_jobq = 1;
+ // A last packet event was detected
+ // printf("last packet event detected\n"); fflush(stdout);
+ this_ireport->reporter_thread_suspends = consumption_detector.reporter_thread_suspends;
+ if (this_ireport->packet_handler_pre_report) {
+ (*this_ireport->packet_handler_pre_report)(this_ireport, packet);
+ }
+ if (this_ireport->packet_handler_post_report) {
+ (*this_ireport->packet_handler_post_report)(this_ireport, packet);
+ }
+ this_ireport->info.ts.packetTime = packet->packetTime;
+ assert(this_ireport->transfer_protocol_handler != NULL);
+ (*this_ireport->transfer_protocol_handler)(this_ireport, 1);
+ // This is a final report so set the sum report header's packet time
+ // Note, the thread with the max value will set this
+ if (fullduplexstats && isEnhanced(this_ireport->info.common)) {
+ // The largest packet timestamp sets the sum report final time
+ if (TimeDifference(fullduplexstats->ts.packetTime, packet->packetTime) > 0) {
+ fullduplexstats->ts.packetTime = packet->packetTime;
+ }
+ if (DecrSumReportRefCounter(this_ireport->FullDuplexReport) == 0) {
+ if (this_ireport->FullDuplexReport->transfer_protocol_sum_handler) {
+ (*this_ireport->FullDuplexReport->transfer_protocol_sum_handler)(fullduplexstats, 1);
+ }
+ // FullDuplex report gets freed by a traffic thread (per its barrier)
+ }
+ }
+ if (sumstats) {
+ if (!this_ireport->GroupSumReport->threads_cntr_fsum)
+ this_ireport->GroupSumReport->threads_cntr_fsum = this_ireport->GroupSumReport->reference.maxcount;
+ if (TimeDifference(sumstats->ts.packetTime, packet->packetTime) > 0) {
+ sumstats->ts.packetTime = packet->packetTime;
+ }
+ if (this_ireport->GroupSumReport->transfer_protocol_sum_handler && \
+ (--this_ireport->GroupSumReport->threads_cntr_fsum == 0) && (this_ireport->GroupSumReport->reference.maxcount > 1)) {
+ (*this_ireport->GroupSumReport->transfer_protocol_sum_handler)(&this_ireport->GroupSumReport->info, 1);
+ }
+ }
+ }
+ }
+ return need_free;
+}
+/*
+ * Process reports
+ *
+ * Make notice here, the reporter thread is freeing most reports, traffic threads
+ * can't use them anymore (except for the DATA REPORT);
+ *
+ */
+inline int reporter_process_report (struct ReportHeader *reporthdr) {
+ assert(reporthdr != NULL);
+ int done = 1;
+ switch (reporthdr->type) {
+ case DATA_REPORT:
+ done = reporter_process_transfer_report((struct ReporterData *)reporthdr->this_report);
+ fflush(stdout);
+ if (done) {
+ struct ReporterData *tmp = (struct ReporterData *)reporthdr->this_report;
+ struct PacketRing *pr = tmp->packetring;
+ pr->consumerdone = 1;
+ // Data Reports are special because the traffic thread needs to free them, just signal
+ Condition_Signal(pr->awake_producer);
+ }
+ break;
+ case CONNECTION_REPORT:
+ {
+ struct ConnectionInfo *creport = (struct ConnectionInfo *)reporthdr->this_report;
+ assert(creport!=NULL);
+ if (!isCompat(creport->common) && (creport->common->ThreadMode == kMode_Client) && myConnectionReport) {
+ // Clients' connect times will be inputs to the overall connect stats
+ if (creport->tcpinitstats.connecttime > 0.0) {
+ reporter_update_mmm(&myConnectionReport->connect_times, creport->tcpinitstats.connecttime);
+ } else {
+ myConnectionReport->connect_times.err++;
+ }
+ }
+ reporter_print_connection_report(creport);
+ fflush(stdout);
+ FreeReport(reporthdr);
+ }
+ break;
+ case SETTINGS_REPORT:
+ reporter_print_settings_report((struct ReportSettings *)reporthdr->this_report);
+ fflush(stdout);
+ FreeReport(reporthdr);
+ break;
+ case SERVER_RELAY_REPORT:
+ reporter_print_server_relay_report((struct ServerRelay *)reporthdr->this_report);
+ fflush(stdout);
+ FreeReport(reporthdr);
+ break;
+ default:
+ fprintf(stderr,"Invalid report type in process report %p\n", reporthdr->this_report);
+ assert(0);
+ break;
+ }
+#ifdef HAVE_THREAD_DEBUG
+ // thread_debug("Processed report %p type=%d", (void *)reporthdr, reporthdr->report.type);
+#endif
+ return done;
+}
+
+/*
+ * Updates connection stats
+ */
+#define L2DROPFILTERCOUNTER 100
+
+// Reporter private routines
+void reporter_handle_packet_null (struct ReporterData *data, struct ReportStruct *packet) {
+}
+void reporter_transfer_protocol_null (struct ReporterData *data, int final){
+}
+
+static inline void reporter_compute_packet_pps (struct TransferInfo *stats, struct ReportStruct *packet) {
+ if (!packet->emptyreport) {
+ stats->total.Datagrams.current++;
+ stats->total.IPG.current++;
+ }
+ stats->ts.IPGstart = packet->packetTime;
+ stats->IPGsum += TimeDifference(packet->packetTime, packet->prevPacketTime);
+#ifdef DEBUG_PPS
+ printf("*** IPGsum = %f cnt=%ld ipg=%ld.%ld pkt=%ld.%ld id=%ld empty=%d transit=%f prev=%ld.%ld\n", stats->IPGsum, stats->cntIPG, stats->ts.IPGstart.tv_sec, stats->ts.IPGstart.tv_usec, packet->packetTime.tv_sec, packet->packetTime.tv_usec, packet->packetID, packet->emptyreport, TimeDifference(packet->packetTime, packet->prevPacketTime), packet->prevPacketTime.tv_sec, packet->prevPacketTime.tv_usec);
+#endif
+}
+
+static void reporter_handle_packet_oneway_transit (struct TransferInfo *stats, struct ReportStruct *packet) {
+ // Transit or latency updates done inline below
+ double transit = TimeDifference(packet->packetTime, packet->sentTime);
+ if (stats->latency_histogram) {
+ histogram_insert(stats->latency_histogram, transit, NULL);
+ }
+ double deltaTransit;
+ deltaTransit = transit - stats->transit.current.last;
+ stats->transit.current.last = transit; // shift transit for next time
+ if (deltaTransit < 0.0) {
+ deltaTransit = -deltaTransit;
+ }
+ // Compute end/end delay stats
+ reporter_update_mmm(&stats->transit.total, transit);
+ reporter_update_mmm(&stats->transit.current, transit);
+ //
+ // Compute jitter though filter the case of isoch and between isoch frames
+ // or, in other words, only calculate jitter for packets within the same isoch frame
+ //
+ // Taken from RFC 1889, Real Time Protocol (RTP)
+ // J = J + ( | D(i-1,i) | - J ) /
+ //
+ // interarrival jitter:
+ //
+ // An estimate of the statistical variance of the RTP data packet
+ // interarrival time, measured in timestamp units and expressed as
+ // an unsigned integer. The interarrival jitter J is defined to be
+ // the mean deviation (smoothed absolute value) of the difference D
+ // in packet spacing at the receiver compared to the sender for a
+ // pair of packets. As shown in the equation below, this is
+ // equivalent to the difference in the "relative transit time" for
+ // the two packets; the relative transit time is the difference
+ // between a packet's RTP timestamp and the receiver's clock at the
+ // time of arrival, measured in the same units.
+ //
+ // If Si is the RTP timestamp from packet i, and Ri is the time of
+ // arrival in RTP timestamp units for packet i, then for two packets i
+ // and j, D may be expressed as
+ //
+ // D(i,j)=(Rj-Ri)-(Sj-Si)=(Rj-Sj)-(Ri-Si)
+ //
+ // The interarrival jitter is calculated continuously as each data
+ // packet i is received from source SSRC_n, using this difference D for
+ // that packet and the previous packet i-1 in order of arrival (not
+ // necessarily in sequence), according to the formula
+ //
+ // J=J+(|D(i-1,i)|-J)/16
+ //
+ // Whenever a reception report is issued, the current value of J is
+ // sampled. This algorithm is the optimal first-order estimator and
+ // the gain parameter 1/16 gives a good noise reduction ratio while /
+ // maintaining a reasonable rate of convergence
+ //
+ if (isIsochronous(stats->common) && stats->isochstats.newburst) {
+ --stats->isochstats.newburst; // decr the burst counter, need for RTP estimator w/isoch
+ // printf("**** skip value %f per frame change packet %d expected %d max = %f %d\n", deltaTransit, packet->frameID, stats->isochstats.frameID, stats->inline_jitter.total.max, stats->isochstats.newburst);
+ } else if (stats->transit.total.cnt > 1) {
+ stats->jitter += (deltaTransit - stats->jitter) / (16.0);
+ reporter_update_mmm(&stats->inline_jitter.total, stats->jitter);
+ reporter_update_mmm(&stats->inline_jitter.current, stats->jitter);
+ if (stats->jitter_histogram) {
+ histogram_insert(stats->jitter_histogram, deltaTransit, NULL);
+ }
+ }
+}
+
+
+static void reporter_handle_isoch_oneway_transit_tcp (struct TransferInfo *stats, struct ReportStruct *packet) {
+ // printf("fid=%lu bs=%lu remain=%lu\n", packet->frameID, packet->burstsize, packet->remaining);
+ if (packet->frameID && packet->transit_ready) {
+ int framedelta = 0;
+ double frametransit = 0;
+ // very first isochronous frame
+ if (!stats->isochstats.frameID) {
+ stats->isochstats.framecnt.current=packet->frameID;
+ stats->isochstats.newburst = 0; // no packet/read filtering of early samples for TCP
+ }
+ // perform client and server frame based accounting
+ if ((framedelta = (packet->frameID - stats->isochstats.frameID))) {
+ stats->isochstats.framecnt.current++;
+ if (framedelta > 1) {
+ stats->isochstats.framelostcnt.current += (framedelta-1);
+ stats->isochstats.slipcnt.current++;
+ } else if (stats->common->ThreadMode == kMode_Server) {
+ // Triptimes use the frame start time in passed in the frame header while
+ // it's calculated from the very first start time and frame id w/o trip timees
+ if (isTripTime(stats->common)) {
+ frametransit = TimeDifference(packet->packetTime, packet->isochStartTime);
+ } else {
+ frametransit = TimeDifference(packet->packetTime, packet->isochStartTime) \
+ - ((packet->burstperiod * (packet->frameID - 1)) / 1e6);
+ }
+ reporter_update_mmm(&stats->isochstats.transit.total, frametransit);
+ reporter_update_mmm(&stats->isochstats.transit.current, frametransit);
+ if (stats->framelatency_histogram) {
+ histogram_insert(stats->framelatency_histogram, frametransit, &packet->packetTime);
+ }
+ }
+ }
+ stats->isochstats.frameID = packet->frameID;
+ }
+}
+
+static void reporter_handle_isoch_oneway_transit_udp (struct TransferInfo *stats, struct ReportStruct *packet) {
+ // printf("fid=%lu bs=%lu remain=%lu\n", packet->frameID, packet->burstsize, packet->remaining);
+ if (packet->frameID && packet->transit_ready) {
+ int framedelta = 0;
+ double frametransit = 0;
+ // very first isochronous frame
+ if (!stats->isochstats.frameID) {
+ stats->isochstats.framecnt.current=1;
+ }
+ // perform client and server frame based accounting
+ framedelta = (packet->frameID - stats->isochstats.frameID);
+ stats->isochstats.framecnt.current++;
+// stats->matchframeID = packet->frameID + 1;
+ if (framedelta == 1) {
+ stats->isochstats.newburst = 2; // set to two per RTP's pair calculation
+ // Triptimes use the frame start time in passed in the frame header while
+ // it's calculated from the very first start time and frame id w/o trip timees
+ frametransit = TimeDifference(packet->packetTime, packet->isochStartTime) \
+ - ((packet->burstperiod * (packet->frameID - 1)) / 1e6);
+ reporter_update_mmm(&stats->isochstats.transit.total, frametransit);
+ reporter_update_mmm(&stats->isochstats.transit.current, frametransit);
+ if (stats->framelatency_histogram) {
+ histogram_insert(stats->framelatency_histogram, frametransit, &packet->packetTime);
+ }
+ } else if (framedelta > 1) {
+ stats->isochstats.newburst = 2; // set to two per RTP's pair calculation
+ if (stats->common->ThreadMode == kMode_Server) {
+ stats->isochstats.framelostcnt.current += framedelta;
+ } else {
+ stats->isochstats.framelostcnt.current += framedelta;
+ stats->isochstats.slipcnt.current++;
+ }
+ }
+ stats->isochstats.frameID = packet->frameID;
+ }
+}
+
+
+static void reporter_handle_rxmsg_oneway_transit (struct TransferInfo *stats, struct ReportStruct *packet) {
+ // very first burst
+ if (!stats->isochstats.frameID) {
+ stats->isochstats.frameID = packet->frameID;
+ }
+ if (packet->frameID && packet->transit_ready) {
+ double transit = TimeDifference(packet->packetTime, packet->sentTime);
+// printf("**** r pt %ld.%ld st %ld.%ld %f\n", packet->packetTime.tv_sec, packet->packetTime.tv_usec, packet->sentTime.tv_sec, packet->sentTime.tv_usec, transit);
+ reporter_update_mmm(&stats->transit.total, transit);
+ reporter_update_mmm(&stats->transit.current, transit);
+ if (stats->framelatency_histogram) {
+ histogram_insert(stats->framelatency_histogram, transit, &packet->sentTime);
+ }
+ if (!TimeZero(stats->ts.prevpacketTime)) {
+ double delta = TimeDifference(packet->sentTime, stats->ts.prevpacketTime);
+ stats->IPGsum += delta;
+ }
+ stats->ts.prevpacketTime = packet->sentTime;
+ stats->isochstats.frameID++; // RJM fix this overload
+ stats->burstid_transition = true;
+ } else if (stats->burstid_transition && packet->frameID && (packet->frameID != stats->isochstats.frameID)) {
+ stats->burstid_transition = false;
+ fprintf(stderr,"%sError: expected burst id %u but got %d\n", \
+ stats->common->transferIDStr, stats->isochstats.frameID + 1, packet->frameID);
+ stats->isochstats.frameID = packet->frameID;
+ }
+}
+
+static inline void reporter_handle_txmsg_oneway_transit (struct TransferInfo *stats, struct ReportStruct *packet) {
+ // very first burst
+ if (!stats->isochstats.frameID) {
+ stats->isochstats.frameID = packet->frameID;
+ }
+ if (!TimeZero(stats->ts.prevpacketTime)) {
+ double delta = TimeDifference(packet->sentTime, stats->ts.prevpacketTime);
+ stats->IPGsum += delta;
+ }
+ if (packet->transit_ready) {
+ reporter_handle_packet_oneway_transit(stats, packet);
+ // printf("***Burst id = %ld, transit = %f\n", packet->frameID, stats->transit.lastTransit);
+ if (isIsochronous(stats->common)) {
+ if (packet->frameID && (packet->frameID != (stats->isochstats.frameID + 1))) {
+ fprintf(stderr,"%sError: expected burst id %u but got %d\n", \
+ stats->common->transferIDStr, stats->isochstats.frameID + 1, packet->frameID);
+ }
+ stats->isochstats.frameID = packet->frameID;
+ }
+ }
+}
+
+static void reporter_handle_frame_isoch_oneway_transit (struct TransferInfo *stats, struct ReportStruct *packet) {
+ // printf("fid=%lu bs=%lu remain=%lu\n", packet->frameID, packet->burstsize, packet->remaining);
+ if (packet->scheduled) {
+ reporter_update_mmm(&stats->schedule_error, (double)(packet->sched_err));
+ }
+ if (packet->frameID && packet->transit_ready) {
+ int framedelta=0;
+ // very first isochronous frame
+ if (!stats->isochstats.frameID) {
+ stats->isochstats.framecnt.current=packet->frameID;
+ }
+ // perform frame based accounting
+ if ((framedelta = (packet->frameID - stats->isochstats.frameID))) {
+ stats->isochstats.framecnt.current++;
+ if (framedelta > 1) {
+ stats->isochstats.framelostcnt.current += (framedelta-1);
+ stats->isochstats.slipcnt.current++;
+ }
+ }
+ stats->isochstats.frameID = packet->frameID;
+ }
+}
+
+// This is done in reporter thread context
+void reporter_handle_packet_client (struct ReporterData *data, struct ReportStruct *packet) {
+ struct TransferInfo *stats = &data->info;
+ stats->ts.packetTime = packet->packetTime;
+ if (!packet->emptyreport) {
+ stats->total.Bytes.current += packet->packetLen;
+ if (packet->errwrite && (packet->errwrite != WriteErrNoAccount)) {
+ stats->sock_callstats.write.WriteErr++;
+ stats->sock_callstats.write.totWriteErr++;
+ }
+ // These are valid packets that need standard iperf accounting
+ stats->sock_callstats.write.WriteCnt += packet->writecnt;
+ stats->sock_callstats.write.totWriteCnt += packet->writecnt;
+ if (isIsochronous(stats->common)) {
+ reporter_handle_frame_isoch_oneway_transit(stats, packet);
+ } else if (isPeriodicBurst(stats->common)) {
+ reporter_handle_txmsg_oneway_transit(stats, packet);
+ }
+ if (isTcpWriteTimes(stats->common) && !isUDP(stats->common) && (packet->write_time > 0)) {
+ reporter_update_mmm(&stats->write_mmm.current, ((double) packet->write_time));
+ reporter_update_mmm(&stats->write_mmm.total, ((double) packet->write_time));
+ if (stats->write_histogram ) {
+ histogram_insert(stats->write_histogram, (1e-6 * packet->write_time), NULL);
+ }
+ }
+ }
+ if (isUDP(stats->common)) {
+ stats->PacketID = packet->packetID;
+ reporter_compute_packet_pps(stats, packet);
+ }
+}
+
+#define DEBUG_BB_TIMESTAMPS 0
+void reporter_handle_packet_bb_client (struct ReporterData *data, struct ReportStruct *packet) {
+ struct TransferInfo *stats = &data->info;
+ if (packet->scheduled) {
+ reporter_update_mmm(&stats->schedule_error, (double)(packet->sched_err));
+ }
+ if (!packet->emptyreport && (packet->packetLen > 0)) {
+ stats->total.Bytes.current += packet->packetLen;
+ double bbrtt = TimeDifference(packet->packetTime, packet->sentTime);
+ double bbowdto = TimeDifference(packet->sentTimeRX, packet->sentTime);
+ double bbowdfro = TimeDifference(packet->packetTime, packet->sentTimeTX);
+ double asym = bbowdfro - bbowdto;
+ stats->ts.prevpacketTime = packet->packetTime;
+#if DEBUG_BB_TIMESTAMPS
+ fprintf(stderr, "BB Debug: ctx=%lx.%lx srx=%lx.%lx stx=%lx.%lx crx=%lx.%lx (hex)\n", packet->sentTime.tv_sec, packet->sentTime.tv_usec, packet->sentTimeRX.tv_sec, packet->sentTimeRX.tv_usec, packet->sentTimeTX.tv_sec, packet->sentTimeTX.tv_usec, packet->packetTime.tv_sec, packet->packetTime.tv_usec);
+ fprintf(stderr, "BB Debug: ctx=%ld.%ld srx=%ld.%ld stx=%ld.%ld crx=%ld.%ld\n", packet->sentTime.tv_sec, packet->sentTime.tv_usec, packet->sentTimeRX.tv_sec, packet->sentTimeRX.tv_usec, packet->sentTimeTX.tv_sec, packet->sentTimeTX.tv_usec, packet->packetTime.tv_sec, packet->packetTime.tv_usec);
+ fprintf(stderr, "BB RTT=%f OWDTo=%f OWDFro=%f Asym=%f\n", bbrtt, bbowdto, bbowdfro, asym);
+#endif
+ stats->iBBrunning += bbrtt;
+ stats->fBBrunning += bbrtt;
+ reporter_update_mmm(&stats->bbrtt.current, bbrtt);
+ reporter_update_mmm(&stats->bbrtt.total, bbrtt);
+ reporter_update_mmm(&stats->bbowdto.total, bbowdto);
+ reporter_update_mmm(&stats->bbowdfro.total, bbowdfro);
+ reporter_update_mmm(&stats->bbasym.total, fabs(asym));
+ if (stats->bbrtt_histogram) {
+ histogram_insert(stats->bbrtt_histogram, bbrtt, NULL);
+ }
+ }
+}
+
+void reporter_handle_packet_bb_server (struct ReporterData *data, struct ReportStruct *packet) {
+ struct TransferInfo *stats = &data->info;
+ stats->ts.packetTime = packet->packetTime;
+ if (!packet->emptyreport && (packet->packetLen > 0)) {
+ stats->total.Bytes.current += packet->packetLen;
+ }
+}
+
+inline void reporter_handle_packet_server_tcp (struct ReporterData *data, struct ReportStruct *packet) {
+ struct TransferInfo *stats = &data->info;
+ if (packet->packetLen > 0) {
+ int bin;
+ stats->total.Bytes.current += packet->packetLen;
+ // mean min max tests
+ stats->sock_callstats.read.cntRead++;
+ stats->sock_callstats.read.totcntRead++;
+ bin = (int)floor((packet->packetLen -1)/stats->sock_callstats.read.binsize);
+ if (bin < TCPREADBINCOUNT) {
+ stats->sock_callstats.read.bins[bin]++;
+ stats->sock_callstats.read.totbins[bin]++;
+ }
+ if (packet->transit_ready) {
+ if (isIsochronous(stats->common) && packet->frameID) {
+ reporter_handle_isoch_oneway_transit_tcp(stats, packet);
+ } else if (isPeriodicBurst(stats->common) || isTripTime(stats->common)) {
+ reporter_handle_rxmsg_oneway_transit(stats, packet);
+ }
+ }
+ }
+}
+
+inline void reporter_handle_packet_server_udp (struct ReporterData *data, struct ReportStruct *packet) {
+ struct TransferInfo *stats = &data->info;
+ stats->ts.packetTime = packet->packetTime;
+ if (packet->emptyreport && (stats->transit.current.cnt == 0)) {
+ // This is the case when empty reports
+ // cross the report interval boundary
+ // Hence, set the per interval min to infinity
+ // and the per interval max and sum to zero
+ reporter_reset_mmm(&stats->transit.current);
+ } else if (packet->packetID > 0) {
+ stats->total.Bytes.current += packet->packetLen;
+ // These are valid packets that need standard iperf accounting
+ // Do L2 accounting first (if needed)
+ if (packet->l2errors && (stats->total.Datagrams.current > L2DROPFILTERCOUNTER)) {
+ stats->l2counts.cnt++;
+ stats->l2counts.tot_cnt++;
+ if (packet->l2errors & L2UNKNOWN) {
+ stats->l2counts.unknown++;
+ stats->l2counts.tot_unknown++;
+ }
+ if (packet->l2errors & L2LENERR) {
+ stats->l2counts.lengtherr++;
+ stats->l2counts.tot_lengtherr++;
+ }
+ if (packet->l2errors & L2CSUMERR) {
+ stats->l2counts.udpcsumerr++;
+ stats->l2counts.tot_udpcsumerr++;
+ }
+ }
+ // packet loss occured if the datagram numbers aren't sequential
+ if (packet->packetID != stats->PacketID + 1) {
+ if (packet->packetID < stats->PacketID + 1) {
+ stats->total.OutofOrder.current++;
+ } else {
+ stats->total.Lost.current += packet->packetID - stats->PacketID - 1;
+ }
+ }
+ // never decrease datagramID (e.g. if we get an out-of-order packet)
+ if (packet->packetID > stats->PacketID) {
+ stats->PacketID = packet->packetID;
+ }
+ reporter_compute_packet_pps(stats, packet);
+ reporter_handle_packet_oneway_transit(stats, packet);
+ if (packet->transit_ready) {
+ if (isIsochronous(stats->common)) {
+ reporter_handle_isoch_oneway_transit_udp(stats, packet);
+ } else if (isPeriodicBurst(stats->common)) {
+ reporter_handle_txmsg_oneway_transit(stats, packet);
+ }
+ }
+ }
+}
+
+// This is done in reporter thread context
+#if HAVE_TCP_STATS
+static inline void reporter_handle_packet_tcpistats (struct ReporterData *data, struct ReportStruct *packet) {
+ assert(data!=NULL);
+ struct TransferInfo *stats = &data->info;
+ stats->sock_callstats.write.tcpstats.retry += (packet->tcpstats.retry_tot - stats->sock_callstats.write.tcpstats.retry_prev);
+ stats->sock_callstats.write.tcpstats.retry_prev = packet->tcpstats.retry_tot;
+ stats->sock_callstats.write.tcpstats.retry_tot = packet->tcpstats.retry_tot;
+ stats->sock_callstats.write.tcpstats.cwnd = packet->tcpstats.cwnd;
+ stats->sock_callstats.write.tcpstats.rtt = packet->tcpstats.rtt;
+ stats->sock_callstats.write.tcpstats.rttvar = packet->tcpstats.rttvar;
+}
+#endif
+
+
+/*
+ * Report printing routines below
+ */
+static inline void reporter_set_timestamps_time (struct TransferInfo *stats, enum TimeStampType tstype) {
+ // There is a corner case when the first packet is also the last where the start time (which comes
+ // from app level syscall) is greater than the packetTime (which come for kernel level SO_TIMESTAMP)
+ // For this case set the start and end time to both zero.
+ struct ReportTimeStamps *times = &stats->ts;
+ if (TimeDifference(times->packetTime, times->startTime) < 0) {
+ times->iEnd = 0;
+ times->iStart = 0;
+ } else {
+ switch (tstype) {
+ case INTERVAL:
+ times->iStart = times->iEnd;
+ times->iEnd = TimeDifference(times->nextTime, times->startTime);
+ TimeAdd(times->nextTime, times->intervalTime);
+ stats->final = false;
+ break;
+ case TOTAL:
+ times->iStart = 0;
+ times->iEnd = TimeDifference(times->packetTime, times->startTime);
+ stats->final = true;
+ break;
+ case FINALPARTIAL:
+ times->iStart = times->iEnd;
+ times->iEnd = TimeDifference(times->packetTime, times->startTime);
+ stats->final = false;
+ break;
+ case INTERVALPARTIAL:
+ if ((times->iStart = TimeDifference(times->prevpacketTime, times->startTime)) < 0)
+ times->iStart = 0.0;
+ times->iEnd = TimeDifference(times->packetTime, times->startTime);
+ stats->final = false;
+ break;
+ default:
+ times->iEnd = -1;
+ times->iStart = -1;
+ stats->final = false;
+ break;
+ }
+ }
+}
+
+// If reports were missed, catch up now
+static inline void reporter_transfer_protocol_missed_reports (struct TransferInfo *stats, struct ReportStruct *packet) {
+ while (TimeDifference(packet->packetTime, stats->ts.nextTime) > TimeDouble(stats->ts.intervalTime)) {
+// printf("**** cmp=%f/%f next %ld.%ld packet %ld.%ld id=%ld\n", TimeDifference(packet->packetTime, stats->ts.nextTime), TimeDouble(stats->ts.intervalTime), stats->ts.nextTime.tv_sec, stats->ts.nextTime.tv_usec, packet->packetTime.tv_sec, packet->packetTime.tv_usec, packet->packetID);
+ reporter_set_timestamps_time(stats, INTERVAL);
+ struct TransferInfo emptystats;
+ memset(&emptystats, 0, sizeof(struct TransferInfo));
+ emptystats.ts.iStart = stats->ts.iStart;
+ emptystats.ts.iEnd = stats->ts.iEnd;
+ emptystats.common = stats->common;
+ if ((stats->output_handler) && !(stats->isMaskOutput))
+ (*stats->output_handler)(&emptystats);
+ }
+}
+
+static inline void reporter_reset_transfer_stats_client_tcp (struct TransferInfo *stats) {
+ stats->total.Bytes.prev = stats->total.Bytes.current;
+ stats->sock_callstats.write.WriteCnt = 0;
+ stats->sock_callstats.write.WriteErr = 0;
+ stats->isochstats.framecnt.prev = stats->isochstats.framecnt.current;
+ stats->isochstats.framelostcnt.prev = stats->isochstats.framelostcnt.current;
+ stats->isochstats.slipcnt.prev = stats->isochstats.slipcnt.current;
+#if HAVE_TCP_STATS
+ // set the interval retry counter to zero
+ stats->sock_callstats.write.tcpstats.retry = 0;
+#endif
+ if (isBounceBack(stats->common)) {
+ stats->iBBrunning = 0;
+ reporter_reset_mmm(&stats->bbrtt.current);
+ reporter_reset_mmm(&stats->bbowdto.current);
+ reporter_reset_mmm(&stats->bbowdfro.current);
+ reporter_reset_mmm(&stats->bbasym.current);
+ }
+ if (isTcpWriteTimes(stats->common)) {
+ stats->write_mmm.current.cnt = 0;
+ stats->write_mmm.current.min = FLT_MAX;
+ stats->write_mmm.current.max = FLT_MIN;
+ stats->write_mmm.current.sum = 0;
+ stats->write_mmm.current.vd = 0;
+ stats->write_mmm.current.mean = 0;
+ stats->write_mmm.current.m2 = 0;
+ }
+}
+
+static inline void reporter_reset_transfer_stats_client_udp (struct TransferInfo *stats) {
+ if (stats->cntError < 0) {
+ stats->cntError = 0;
+ }
+ stats->total.Lost.prev = stats->total.Lost.current;
+ stats->total.Datagrams.prev = stats->total.Datagrams.current;
+ stats->total.Bytes.prev = stats->total.Bytes.current;
+ stats->total.IPG.prev = stats->total.IPG.current;
+ stats->sock_callstats.write.WriteCnt = 0;
+ stats->sock_callstats.write.WriteErr = 0;
+ stats->isochstats.framecnt.prev = stats->isochstats.framecnt.current;
+ stats->isochstats.framelostcnt.prev = stats->isochstats.framelostcnt.current;
+ stats->isochstats.slipcnt.prev = stats->isochstats.slipcnt.current;
+ if (stats->cntDatagrams)
+ stats->IPGsum = 0;
+}
+
+static inline void reporter_reset_transfer_stats_server_tcp (struct TransferInfo *stats) {
+ int ix;
+ stats->total.Bytes.prev = stats->total.Bytes.current;
+ stats->sock_callstats.read.cntRead = 0;
+ for (ix = 0; ix < 8; ix++) {
+ stats->sock_callstats.read.bins[ix] = 0;
+ }
+ reporter_reset_mmm(&stats->transit.current);
+ reporter_reset_mmm(&stats->isochstats.transit.current);
+ stats->IPGsum = 0;
+}
+
+static inline void reporter_reset_transfer_stats_server_udp (struct TransferInfo *stats) {
+ // Reset the enhanced stats for the next report interval
+ stats->total.Bytes.prev = stats->total.Bytes.current;
+ stats->total.Datagrams.prev = stats->PacketID;
+ stats->total.OutofOrder.prev = stats->total.OutofOrder.current;
+ stats->total.Lost.prev = stats->total.Lost.current;
+ stats->total.IPG.prev = stats->total.IPG.current;
+ reporter_reset_mmm(&stats->transit.current);
+ stats->isochstats.framecnt.prev = stats->isochstats.framecnt.current;
+ stats->isochstats.framelostcnt.prev = stats->isochstats.framelostcnt.current;
+ stats->isochstats.slipcnt.prev = stats->isochstats.slipcnt.current;
+ stats->l2counts.cnt = 0;
+ stats->l2counts.unknown = 0;
+ stats->l2counts.udpcsumerr = 0;
+ stats->l2counts.lengtherr = 0;
+ stats->threadcnt = 0;
+ stats->iInP = 0;
+ if (stats->cntDatagrams)
+ stats->IPGsum = 0;
+}
+
+// These do the following
+//
+// o) set the TransferInfo struct and then calls the individual report output handler
+// o) updates the sum and fullduplex reports
+//
+void reporter_transfer_protocol_server_udp (struct ReporterData *data, int final) {
+ struct TransferInfo *stats = &data->info;
+ struct TransferInfo *sumstats = (data->GroupSumReport != NULL) ? &data->GroupSumReport->info : NULL;
+ struct TransferInfo *fullduplexstats = (data->FullDuplexReport != NULL) ? &data->FullDuplexReport->info : NULL;
+ // print a interval report and possibly a partial interval report if this a final
+ stats->cntBytes = stats->total.Bytes.current - stats->total.Bytes.prev;
+ stats->cntOutofOrder = stats->total.OutofOrder.current - stats->total.OutofOrder.prev;
+ // assume most of the time out-of-order packets are
+ // duplicate packets, so conditionally subtract them from the lost packets.
+ stats->cntError = stats->total.Lost.current - stats->total.Lost.prev - stats->cntOutofOrder;
+ if (stats->cntError < 0)
+ stats->cntError = 0;
+ stats->cntDatagrams = stats->PacketID - stats->total.Datagrams.prev;
+ stats->cntIPG = stats->total.IPG.current - stats->total.IPG.prev;
+
+ if (stats->latency_histogram) {
+ stats->latency_histogram->final = final;
+ }
+ if (stats->jitter_histogram) {
+ stats->jitter_histogram->final = final;
+ }
+ if (isIsochronous(stats->common)) {
+ stats->isochstats.cntFrames = stats->isochstats.framecnt.current - stats->isochstats.framecnt.prev;
+ stats->isochstats.cntFramesMissed = stats->isochstats.framelostcnt.current - stats->isochstats.framelostcnt.prev;
+ stats->isochstats.cntSlips = stats->isochstats.slipcnt.current - stats->isochstats.slipcnt.prev;
+ if (stats->framelatency_histogram) {
+ stats->framelatency_histogram->final = final;
+ }
+ }
+ if (stats->total.Datagrams.current == 1)
+ stats->jitter = 0;
+ if (isTripTime(stats->common) && !final) {
+ double lambda = ((stats->IPGsum > 0.0) ? (round (stats->cntIPG / stats->IPGsum)) : 0.0);
+ double meantransit = (double) ((stats->transit.current.cnt > 0) ? (stats->transit.current.sum / stats->transit.current.cnt) : 0.0);
+ double variance = (stats->transit.current.cnt < 2) ? 0 : \
+ (sqrt(stats->transit.current.m2 / (stats->transit.current.cnt - 1)));
+ stats->iInP = (double) lambda * meantransit;
+ stats->iInPVar = (double) lambda * variance;
+ }
+ if (sumstats) {
+ sumstats->total.OutofOrder.current += stats->total.OutofOrder.current - stats->total.OutofOrder.prev;
+ // assume most of the time out-of-order packets are not
+ // duplicate packets, so conditionally subtract them from the lost packets.
+ sumstats->total.Lost.current += stats->total.Lost.current - stats->total.Lost.prev;
+ sumstats->total.Datagrams.current += stats->PacketID - stats->total.Datagrams.prev;
+ sumstats->total.Bytes.current += stats->cntBytes;
+ sumstats->total.IPG.current += stats->cntIPG;
+ if (sumstats->IPGsum < stats->IPGsum)
+ sumstats->IPGsum = stats->IPGsum;
+ sumstats->threadcnt++;
+ sumstats->iInP += stats->iInP;
+ }
+ if (fullduplexstats) {
+ fullduplexstats->total.Bytes.current += stats->cntBytes;
+ fullduplexstats->total.IPG.current += stats->cntIPG;
+ fullduplexstats->total.Datagrams.current += (stats->total.Datagrams.current - stats->total.Datagrams.prev);
+ if (fullduplexstats->IPGsum < stats->IPGsum)
+ fullduplexstats->IPGsum = stats->IPGsum;
+ }
+ if (final) {
+ if ((stats->cntBytes > 0) && !TimeZero(stats->ts.intervalTime)) {
+ stats->cntOutofOrder = stats->total.OutofOrder.current - stats->total.OutofOrder.prev;
+ // assume most of the time out-of-order packets are not
+ // duplicate packets, so conditionally subtract them from the lost packets.
+ stats->cntError = stats->total.Lost.current - stats->total.Lost.prev;
+ stats->cntError -= stats->cntOutofOrder;
+ if (stats->cntError < 0)
+ stats->cntError = 0;
+ stats->cntDatagrams = stats->PacketID - stats->total.Datagrams.prev;
+ if ((stats->output_handler) && !(stats->isMaskOutput)) {
+ reporter_set_timestamps_time(stats, FINALPARTIAL);
+ if ((stats->ts.iEnd - stats->ts.iStart) > stats->ts.significant_partial)
+ (*stats->output_handler)(stats);
+ }
+ }
+ reporter_set_timestamps_time(stats, TOTAL);
+ stats->IPGsum = TimeDifference(stats->ts.packetTime, stats->ts.startTime);
+ stats->cntOutofOrder = stats->total.OutofOrder.current;
+ // assume most of the time out-of-order packets are not
+ // duplicate packets, so conditionally subtract them from the lost packets.
+ stats->cntError = stats->total.Lost.current;
+ stats->cntError -= stats->cntOutofOrder;
+ if (stats->cntError < 0)
+ stats->cntError = 0;
+ stats->cntDatagrams = stats->PacketID;
+ stats->cntIPG = stats->total.IPG.current;
+ stats->IPGsum = TimeDifference(stats->ts.packetTime, stats->ts.startTime);
+ stats->cntBytes = stats->total.Bytes.current;
+ stats->l2counts.cnt = stats->l2counts.tot_cnt;
+ stats->l2counts.unknown = stats->l2counts.tot_unknown;
+ stats->l2counts.udpcsumerr = stats->l2counts.tot_udpcsumerr;
+ stats->l2counts.lengtherr = stats->l2counts.tot_lengtherr;
+ stats->transit.current = stats->transit.total;
+ if (isTripTime(stats->common)) {
+ double lambda = ((stats->IPGsum > 0.0) ? (round (stats->cntIPG / stats->IPGsum)) : 0.0);
+ double meantransit = (double) ((stats->transit.total.cnt > 0) ? (stats->transit.total.sum / stats->transit.total.cnt) : 0.0);
+ double variance = (stats->transit.total.cnt < 2) ? 0 : \
+ (sqrt(stats->transit.total.m2 / (stats->transit.total.cnt - 1)));
+ stats->fInP = (double) lambda * meantransit;
+ stats->fInPVar = (double) lambda * variance;
+ if (sumstats) {
+ sumstats->fInP += stats->fInP;
+ }
+ }
+ if (isIsochronous(stats->common)) {
+ stats->isochstats.cntFrames = stats->isochstats.framecnt.current;
+ stats->isochstats.cntFramesMissed = stats->isochstats.framelostcnt.current;
+ stats->isochstats.cntSlips = stats->isochstats.slipcnt.current;
+ }
+ if (stats->latency_histogram) {
+ if (sumstats && sumstats->latency_histogram) {
+ histogram_add(sumstats->latency_histogram, stats->latency_histogram);
+ sumstats->latency_histogram->final = 1;
+ }
+ stats->latency_histogram->final = 1;
+ }
+ if (stats->jitter_histogram) {
+ if (sumstats && sumstats->jitter_histogram) {
+ histogram_add(sumstats->jitter_histogram, stats->jitter_histogram);
+ sumstats->jitter_histogram->final = 1;
+ }
+ stats->jitter_histogram->final = 1;
+ }
+ if (stats->framelatency_histogram) {
+ stats->framelatency_histogram->final = 1;
+ }
+ }
+ if ((stats->output_handler) && !(stats->isMaskOutput))
+ (*stats->output_handler)(stats);
+ if (!final)
+ reporter_reset_transfer_stats_server_udp(stats);
+}
+
+void reporter_transfer_protocol_sum_server_udp (struct TransferInfo *stats, int final) {
+ if (final) {
+ reporter_set_timestamps_time(stats, TOTAL);
+ stats->cntOutofOrder = stats->total.OutofOrder.current;
+ // assume most of the time out-of-order packets are not
+ // duplicate packets, so conditionally subtract them from the lost packets.
+ stats->cntError = stats->total.Lost.current;
+ stats->cntError -= stats->cntOutofOrder;
+ if (stats->cntError < 0)
+ stats->cntError = 0;
+ stats->cntDatagrams = stats->total.Datagrams.current;
+ stats->cntBytes = stats->total.Bytes.current;
+ stats->IPGsum = TimeDifference(stats->ts.packetTime, stats->ts.startTime);
+ stats->cntIPG = stats->total.IPG.current;
+ } else {
+ stats->cntOutofOrder = stats->total.OutofOrder.current - stats->total.OutofOrder.prev;
+ // assume most of the time out-of-order packets are not
+ // duplicate packets, so conditionally subtract them from the lost packets.
+ stats->cntError = stats->total.Lost.current - stats->total.Lost.prev;
+ stats->cntError -= stats->cntOutofOrder;
+ if (stats->cntError < 0)
+ stats->cntError = 0;
+ stats->cntDatagrams = stats->total.Datagrams.current - stats->total.Datagrams.prev;
+ stats->cntBytes = stats->total.Bytes.current - stats->total.Bytes.prev;
+ stats->cntIPG = stats->total.IPG.current - stats->total.IPG.prev;
+ }
+ if ((stats->output_handler) && !(stats->isMaskOutput))
+ (*stats->output_handler)(stats);
+ if (!final) {
+ // there is no packet ID for sum server reports, set it to total cnt for calculation
+ stats->PacketID = stats->total.Datagrams.current;
+ reporter_reset_transfer_stats_server_udp(stats);
+ }
+}
+void reporter_transfer_protocol_sum_client_udp (struct TransferInfo *stats, int final) {
+ if (final) {
+ reporter_set_timestamps_time(stats, TOTAL);
+ stats->sock_callstats.write.WriteErr = stats->sock_callstats.write.totWriteErr;
+ stats->sock_callstats.write.WriteCnt = stats->sock_callstats.write.totWriteCnt;
+ stats->cntDatagrams = stats->total.Datagrams.current;
+ stats->cntBytes = stats->total.Bytes.current;
+ stats->IPGsum = TimeDifference(stats->ts.packetTime, stats->ts.startTime);
+ stats->cntIPG = stats->total.IPG.current;
+ } else {
+ stats->cntBytes = stats->total.Bytes.current - stats->total.Bytes.prev;
+ stats->cntIPG = stats->total.IPG.current - stats->total.IPG.prev;
+ stats->cntDatagrams = stats->total.Datagrams.current - stats->total.Datagrams.prev;
+ }
+ if ((stats->output_handler) && !(stats->isMaskOutput))
+ (*stats->output_handler)(stats);
+
+ if (!final) {
+ stats->threadcnt = 0;
+ reporter_reset_transfer_stats_client_udp(stats);
+ } else if ((stats->common->ReportMode != kReport_CSV) && !(stats->isMaskOutput)) {
+ printf(report_sumcnt_datagrams, stats->threadcnt, stats->total.Datagrams.current);
+ fflush(stdout);
+ }
+}
+
+void reporter_transfer_protocol_client_udp (struct ReporterData *data, int final) {
+ struct TransferInfo *stats = &data->info;
+ struct TransferInfo *sumstats = (data->GroupSumReport != NULL) ? &data->GroupSumReport->info : NULL;
+ struct TransferInfo *fullduplexstats = (data->FullDuplexReport != NULL) ? &data->FullDuplexReport->info : NULL;
+ stats->cntBytes = stats->total.Bytes.current - stats->total.Bytes.prev;
+ stats->cntDatagrams = stats->total.Datagrams.current - stats->total.Datagrams.prev;
+ stats->cntIPG = stats->total.IPG.current - stats->total.IPG.prev;
+ if (isIsochronous(stats->common)) {
+ stats->isochstats.cntFrames = stats->isochstats.framecnt.current - stats->isochstats.framecnt.prev;
+ stats->isochstats.cntFramesMissed = stats->isochstats.framelostcnt.current - stats->isochstats.framelostcnt.prev;
+ stats->isochstats.cntSlips = stats->isochstats.slipcnt.current - stats->isochstats.slipcnt.prev;
+ }
+ if (sumstats) {
+ sumstats->total.Bytes.current += stats->cntBytes;
+ sumstats->sock_callstats.write.WriteErr += stats->sock_callstats.write.WriteErr;
+ sumstats->sock_callstats.write.WriteCnt += stats->sock_callstats.write.WriteCnt;
+ sumstats->sock_callstats.write.totWriteErr += stats->sock_callstats.write.WriteErr;
+ sumstats->sock_callstats.write.totWriteCnt += stats->sock_callstats.write.WriteCnt;
+ sumstats->total.Datagrams.current += stats->cntDatagrams;
+ if (sumstats->IPGsum < stats->IPGsum)
+ sumstats->IPGsum = stats->IPGsum;
+ sumstats->total.IPG.current += stats->cntIPG;
+ sumstats->threadcnt++;
+ }
+ if (fullduplexstats) {
+ fullduplexstats->total.Bytes.current += stats->cntBytes;
+ fullduplexstats->total.IPG.current += stats->cntIPG;
+ fullduplexstats->total.Datagrams.current += stats->cntDatagrams;
+ if (fullduplexstats->IPGsum < stats->IPGsum)
+ fullduplexstats->IPGsum = stats->IPGsum;
+ }
+ if (final) {
+ reporter_set_timestamps_time(stats, TOTAL);
+ stats->cntBytes = stats->total.Bytes.current;
+ stats->sock_callstats.write.WriteErr = stats->sock_callstats.write.totWriteErr;
+ stats->sock_callstats.write.WriteCnt = stats->sock_callstats.write.totWriteCnt;
+ stats->cntIPG = stats->total.IPG.current;
+ stats->cntDatagrams = stats->PacketID;
+ stats->IPGsum = TimeDifference(stats->ts.packetTime, stats->ts.startTime);
+ if (isIsochronous(stats->common)) {
+ stats->isochstats.cntFrames = stats->isochstats.framecnt.current;
+ stats->isochstats.cntFramesMissed = stats->isochstats.framelostcnt.current;
+ stats->isochstats.cntSlips = stats->isochstats.slipcnt.current;
+ }
+ } else {
+ if (stats->ts.iEnd > 0) {
+ stats->cntIPG = (stats->total.IPG.current - stats->total.IPG.prev);
+ } else {
+ stats->cntIPG = 0;
+ }
+ }
+ if ((stats->output_handler) && !(stats->isMaskOutput)) {
+ (*stats->output_handler)(stats);
+ if (final && (stats->common->ReportMode != kReport_CSV)) {
+ printf(report_datagrams, stats->common->transferID, stats->total.Datagrams.current);
+ fflush(stdout);
+ }
+ }
+ reporter_reset_transfer_stats_client_udp(stats);
+}
+
+void reporter_transfer_protocol_server_tcp (struct ReporterData *data, int final) {
+ struct TransferInfo *stats = &data->info;
+ struct TransferInfo *sumstats = (data->GroupSumReport != NULL) ? &data->GroupSumReport->info : NULL;
+ struct TransferInfo *fullduplexstats = (data->FullDuplexReport != NULL) ? &data->FullDuplexReport->info : NULL;
+ stats->cntBytes = stats->total.Bytes.current - stats->total.Bytes.prev;
+ int ix;
+ if (stats->framelatency_histogram) {
+ stats->framelatency_histogram->final = 0;
+ }
+ double thisInP;
+ if (!final) {
+ double bytecnt = (double) (stats->total.Bytes.current - stats->total.Bytes.prev);
+ double lambda = (stats->IPGsum > 0.0) ? (bytecnt / stats->IPGsum) : 0.0;
+ double meantransit = (double) ((stats->transit.current.cnt > 0) ? (stats->transit.current.sum / stats->transit.current.cnt) : 0.0);
+ thisInP = lambda * meantransit;
+ stats->iInP = thisInP;
+ } else {
+ double bytecnt = (double) stats->cntBytes;
+ double lambda = (stats->IPGsum > 0.0) ? (bytecnt / stats->IPGsum) : 0.0;
+ double meantransit = (double) ((stats->transit.total.cnt > 0) ? (stats->transit.total.sum / stats->transit.total.cnt) : 0.0);
+ thisInP = lambda * meantransit;
+ stats->fInP = thisInP;
+ }
+ if (sumstats) {
+ sumstats->threadcnt++;
+ sumstats->total.Bytes.current += stats->cntBytes;
+ sumstats->sock_callstats.read.cntRead += stats->sock_callstats.read.cntRead;
+ sumstats->sock_callstats.read.totcntRead += stats->sock_callstats.read.cntRead;
+ for (ix = 0; ix < TCPREADBINCOUNT; ix++) {
+ sumstats->sock_callstats.read.bins[ix] += stats->sock_callstats.read.bins[ix];
+ sumstats->sock_callstats.read.totbins[ix] += stats->sock_callstats.read.bins[ix];
+ }
+ if (!final) {
+ sumstats->iInP += thisInP;
+ } else {
+ sumstats->fInP += thisInP;
+ }
+ }
+ if (fullduplexstats) {
+ fullduplexstats->total.Bytes.current += stats->cntBytes;
+ }
+ if (final) {
+ if ((stats->cntBytes > 0) && stats->output_handler && !TimeZero(stats->ts.intervalTime)) {
+ // print a partial interval report if enable and this a final
+ if ((stats->output_handler) && !(stats->isMaskOutput)) {
+ if (isIsochronous(stats->common)) {
+ stats->isochstats.cntFrames = stats->isochstats.framecnt.current - stats->isochstats.framecnt.prev;
+ stats->isochstats.cntFramesMissed = stats->isochstats.framelostcnt.current - stats->isochstats.framelostcnt.prev;
+ stats->isochstats.cntSlips = stats->isochstats.slipcnt.current - stats->isochstats.slipcnt.prev;
+ }
+ reporter_set_timestamps_time(stats, FINALPARTIAL);
+ if ((stats->ts.iEnd - stats->ts.iStart) > stats->ts.significant_partial)
+ (*stats->output_handler)(stats);
+ reporter_reset_transfer_stats_server_tcp(stats);
+ }
+ }
+ if (stats->framelatency_histogram) {
+ stats->framelatency_histogram->final = 1;
+ }
+ reporter_set_timestamps_time(stats, TOTAL);
+ stats->cntBytes = stats->total.Bytes.current;
+ stats->IPGsum = stats->ts.iEnd;
+ stats->sock_callstats.read.cntRead = stats->sock_callstats.read.totcntRead;
+ for (ix = 0; ix < TCPREADBINCOUNT; ix++) {
+ stats->sock_callstats.read.bins[ix] = stats->sock_callstats.read.totbins[ix];
+ }
+ if (isIsochronous(stats->common)) {
+ stats->isochstats.cntFrames = stats->isochstats.framecnt.current;
+ stats->isochstats.cntFramesMissed = stats->isochstats.framelostcnt.current;
+ stats->isochstats.cntSlips = stats->isochstats.slipcnt.current;
+ }
+ stats->transit.current = stats->transit.total;
+ if (stats->framelatency_histogram) {
+ if (sumstats && sumstats->framelatency_histogram) {
+ histogram_add(sumstats->framelatency_histogram, stats->framelatency_histogram);
+ sumstats->framelatency_histogram->final = 1;
+ }
+ stats->framelatency_histogram->final = 1;
+ }
+ } else if (isIsochronous(stats->common)) {
+ stats->isochstats.cntFrames = stats->isochstats.framecnt.current - stats->isochstats.framecnt.prev;
+ stats->isochstats.cntFramesMissed = stats->isochstats.framelostcnt.current - stats->isochstats.framelostcnt.prev;
+ stats->isochstats.cntSlips = stats->isochstats.slipcnt.current - stats->isochstats.slipcnt.prev;
+ }
+ if ((stats->output_handler) && !stats->isMaskOutput) {
+ (*stats->output_handler)(stats);
+ if (isFrameInterval(stats->common) && stats->framelatency_histogram) {
+ histogram_print(stats->framelatency_histogram, stats->ts.iStart, stats->ts.iEnd);
+ }
+ }
+ if (!final)
+ reporter_reset_transfer_stats_server_tcp(stats);
+}
+
+void reporter_transfer_protocol_client_tcp (struct ReporterData *data, int final) {
+ struct TransferInfo *stats = &data->info;
+ struct TransferInfo *sumstats = (data->GroupSumReport != NULL) ? &data->GroupSumReport->info : NULL;
+ struct TransferInfo *fullduplexstats = (data->FullDuplexReport != NULL) ? &data->FullDuplexReport->info : NULL;
+ stats->cntBytes = stats->total.Bytes.current - stats->total.Bytes.prev;
+ if (stats->latency_histogram) {
+ stats->latency_histogram->final = final;
+ }
+ if (stats->write_histogram) {
+ stats->write_histogram->final = final;
+ }
+ if (isIsochronous(stats->common)) {
+ if (final) {
+ stats->isochstats.cntFrames = stats->isochstats.framecnt.current;
+ stats->isochstats.cntFramesMissed = stats->isochstats.framelostcnt.current;
+ stats->isochstats.cntSlips = stats->isochstats.slipcnt.current;
+ } else {
+ stats->isochstats.cntFrames = stats->isochstats.framecnt.current - stats->isochstats.framecnt.prev;
+ stats->isochstats.cntFramesMissed = stats->isochstats.framelostcnt.current - stats->isochstats.framelostcnt.prev;
+ stats->isochstats.cntSlips = stats->isochstats.slipcnt.current - stats->isochstats.slipcnt.prev;
+ }
+ }
+ if (sumstats) {
+ sumstats->total.Bytes.current += stats->cntBytes;
+ sumstats->sock_callstats.write.WriteErr += stats->sock_callstats.write.WriteErr;
+ sumstats->sock_callstats.write.WriteCnt += stats->sock_callstats.write.WriteCnt;
+ sumstats->sock_callstats.write.totWriteErr += stats->sock_callstats.write.WriteErr;
+ sumstats->sock_callstats.write.totWriteCnt += stats->sock_callstats.write.WriteCnt;
+ sumstats->threadcnt++;
+#if HAVE_TCP_STATS
+ sumstats->sock_callstats.write.tcpstats.retry += stats->sock_callstats.write.tcpstats.retry;
+ sumstats->sock_callstats.write.tcpstats.retry_tot += stats->sock_callstats.write.tcpstats.retry;
+#endif
+ }
+ if (fullduplexstats) {
+ fullduplexstats->total.Bytes.current += stats->cntBytes;
+ }
+ if (final) {
+ if (stats->latency_histogram) {
+ stats->latency_histogram->final = 1;
+ }
+ if (stats->write_histogram) {
+ stats->write_histogram->final = 1;
+ }
+ if ((stats->cntBytes > 0) && stats->output_handler && !TimeZero(stats->ts.intervalTime)) {
+ // print a partial interval report if enable and this a final
+ if ((stats->output_handler) && !(stats->isMaskOutput)) {
+ if (isIsochronous(stats->common)) {
+ stats->isochstats.cntFrames = stats->isochstats.framecnt.current - stats->isochstats.framecnt.prev;
+ stats->isochstats.cntFramesMissed = stats->isochstats.framelostcnt.current - stats->isochstats.framelostcnt.prev;
+ stats->isochstats.cntSlips = stats->isochstats.slipcnt.current - stats->isochstats.slipcnt.prev;
+ }
+ reporter_set_timestamps_time(stats, FINALPARTIAL);
+ if ((stats->ts.iEnd - stats->ts.iStart) > stats->ts.significant_partial)
+ (*stats->output_handler)(stats);
+ reporter_reset_transfer_stats_client_tcp(stats);
+ }
+ }
+ if (isIsochronous(stats->common)) {
+ stats->isochstats.cntFrames = stats->isochstats.framecnt.current;
+ stats->isochstats.cntFramesMissed = stats->isochstats.framelostcnt.current;
+ stats->isochstats.cntSlips = stats->isochstats.slipcnt.current;
+ }
+ stats->sock_callstats.write.WriteErr = stats->sock_callstats.write.totWriteErr;
+ stats->sock_callstats.write.WriteCnt = stats->sock_callstats.write.totWriteCnt;
+#if HAVE_TCP_STATS
+ stats->sock_callstats.write.tcpstats.retry = stats->sock_callstats.write.tcpstats.retry_tot;
+#endif
+ if (stats->framelatency_histogram) {
+ stats->framelatency_histogram->final = 1;
+ }
+ stats->cntBytes = stats->total.Bytes.current;
+ stats->write_mmm.current = stats->write_mmm.total;
+ reporter_set_timestamps_time(stats, TOTAL);
+ } else if (isIsochronous(stats->common)) {
+ stats->isochstats.cntFrames = stats->isochstats.framecnt.current - stats->isochstats.framecnt.prev;
+ stats->isochstats.cntFramesMissed = stats->isochstats.framelostcnt.current - stats->isochstats.framelostcnt.prev;
+ stats->isochstats.cntSlips = stats->isochstats.slipcnt.current - stats->isochstats.slipcnt.prev;
+ }
+ if ((stats->output_handler) && !(stats->isMaskOutput)) {
+ (*stats->output_handler)(stats);
+ }
+ if (!final)
+ reporter_reset_transfer_stats_client_tcp(stats);
+}
+
+/*
+ * Handles summing of threads
+ */
+void reporter_transfer_protocol_sum_client_tcp (struct TransferInfo *stats, int final) {
+ if (!final || (final && (stats->cntBytes > 0) && !TimeZero(stats->ts.intervalTime))) {
+ stats->cntBytes = stats->total.Bytes.current - stats->total.Bytes.prev;
+ if (final) {
+ if ((stats->output_handler) && !(stats->isMaskOutput)) {
+ reporter_set_timestamps_time(stats, FINALPARTIAL);
+ if ((stats->ts.iEnd - stats->ts.iStart) > stats->ts.significant_partial)
+ (*stats->output_handler)(stats);
+ reporter_reset_transfer_stats_client_tcp(stats);
+ }
+ } else if ((stats->output_handler) && !(stats->isMaskOutput)) {
+ (*stats->output_handler)(stats);
+ stats->threadcnt = 0;
+ }
+ reporter_reset_transfer_stats_client_tcp(stats);
+ }
+ if (final) {
+ stats->sock_callstats.write.WriteErr = stats->sock_callstats.write.totWriteErr;
+ stats->sock_callstats.write.WriteCnt = stats->sock_callstats.write.totWriteCnt;
+#if HAVE_TCP_STATS
+ stats->sock_callstats.write.tcpstats.retry = stats->sock_callstats.write.tcpstats.retry_tot;
+#endif
+ stats->cntBytes = stats->total.Bytes.current;
+ reporter_set_timestamps_time(stats, TOTAL);
+ if ((stats->output_handler) && !(stats->isMaskOutput))
+ (*stats->output_handler)(stats);
+ }
+}
+
+void reporter_transfer_protocol_client_bb_tcp (struct ReporterData *data, int final) {
+ struct TransferInfo *stats = &data->info;
+
+ if (final) {
+ if ((stats->cntBytes > 0) && stats->output_handler && !TimeZero(stats->ts.intervalTime)) {
+ // print a partial interval report if enable and this a final
+ if ((stats->output_handler) && !(stats->isMaskOutput)) {
+ reporter_set_timestamps_time(stats, FINALPARTIAL);
+ if ((stats->ts.iEnd - stats->ts.iStart) > stats->ts.significant_partial)
+ (*stats->output_handler)(stats);
+ reporter_reset_transfer_stats_client_tcp(stats);
+ }
+ }
+#if HAVE_TCP_STATS
+ stats->sock_callstats.write.tcpstats.retry = stats->sock_callstats.write.tcpstats.retry_tot;
+#endif
+ stats->cntBytes = stats->total.Bytes.current;
+ reporter_set_timestamps_time(stats, TOTAL);
+ } else {
+ stats->cntBytes = stats->total.Bytes.current - stats->total.Bytes.prev;
+ }
+ if ((stats->output_handler) && !(stats->isMaskOutput))
+ (*stats->output_handler)(stats);
+ if (!final) {
+ reporter_reset_transfer_stats_client_tcp(stats);
+ }
+}
+
+void reporter_transfer_protocol_server_bb_tcp (struct ReporterData *data, int final) {
+ struct TransferInfo *stats = &data->info;
+ if (final) {
+ if ((stats->cntBytes > 0) && stats->output_handler && !TimeZero(stats->ts.intervalTime)) {
+ // print a partial interval report if enable and this a final
+ if ((stats->output_handler) && !(stats->isMaskOutput)) {
+ reporter_set_timestamps_time(stats, FINALPARTIAL);
+ if ((stats->ts.iEnd - stats->ts.iStart) > stats->ts.significant_partial)
+ (*stats->output_handler)(stats);
+ reporter_reset_transfer_stats_server_tcp(stats);
+ }
+ }
+#if HAVE_TCP_STATS
+
+ stats->sock_callstats.write.tcpstats.retry = stats->sock_callstats.write.tcpstats.retry_tot;
+#endif
+ stats->cntBytes = stats->total.Bytes.current;
+ reporter_set_timestamps_time(stats, TOTAL);
+ } else {
+ stats->cntBytes = stats->total.Bytes.current - stats->total.Bytes.prev;
+ }
+ if ((stats->output_handler) && !(stats->isMaskOutput))
+ (*stats->output_handler)(stats);
+ if (!final)
+ reporter_reset_transfer_stats_client_tcp(stats);
+}
+
+void reporter_transfer_protocol_sum_server_tcp (struct TransferInfo *stats, int final) {
+ if (!final || (final && (stats->cntBytes > 0) && !TimeZero(stats->ts.intervalTime))) {
+ stats->cntBytes = stats->total.Bytes.current - stats->total.Bytes.prev;
+ if (final) {
+ if ((stats->output_handler) && !(stats->isMaskOutput)) {
+ reporter_set_timestamps_time(stats, FINALPARTIAL);
+ if ((stats->ts.iEnd - stats->ts.iStart) > stats->ts.significant_partial)
+ (*stats->output_handler)(stats);
+ }
+ } else if ((stats->output_handler) && !(stats->isMaskOutput)) {
+ (*stats->output_handler)(stats);
+ stats->threadcnt = 0;
+ stats->iInP = 0;
+ }
+ reporter_reset_transfer_stats_server_tcp(stats);
+ }
+ if (final) {
+ int ix;
+ stats->cntBytes = stats->total.Bytes.current;
+ stats->sock_callstats.read.cntRead = stats->sock_callstats.read.totcntRead;
+ for (ix = 0; ix < TCPREADBINCOUNT; ix++) {
+ stats->sock_callstats.read.bins[ix] = stats->sock_callstats.read.totbins[ix];
+ }
+ stats->cntBytes = stats->total.Bytes.current;
+ reporter_set_timestamps_time(stats, TOTAL);
+ if ((stats->output_handler) && !(stats->isMaskOutput))
+ (*stats->output_handler)(stats);
+ }
+}
+void reporter_transfer_protocol_fullduplex_tcp (struct TransferInfo *stats, int final) {
+ if (!final || (final && (stats->cntBytes > 0) && !TimeZero(stats->ts.intervalTime))) {
+ stats->cntBytes = stats->total.Bytes.current - stats->total.Bytes.prev;
+ if (final) {
+ if ((stats->output_handler) && !(stats->isMaskOutput)) {
+ reporter_set_timestamps_time(stats, FINALPARTIAL);
+ if ((stats->ts.iEnd - stats->ts.iStart) > stats->ts.significant_partial)
+ (*stats->output_handler)(stats);
+ }
+ }
+ stats->total.Bytes.prev = stats->total.Bytes.current;
+ }
+ if (final) {
+ stats->cntBytes = stats->total.Bytes.current;
+ reporter_set_timestamps_time(stats, TOTAL);
+ } else {
+ reporter_set_timestamps_time(stats, INTERVAL);
+ }
+ if ((stats->output_handler) && !(stats->isMaskOutput))
+ (*stats->output_handler)(stats);
+}
+
+void reporter_transfer_protocol_fullduplex_udp (struct TransferInfo *stats, int final) {
+ if (!final || (final && (stats->cntBytes > 0) && !TimeZero(stats->ts.intervalTime))) {
+ stats->cntBytes = stats->total.Bytes.current - stats->total.Bytes.prev;
+ stats->cntDatagrams = stats->total.Datagrams.current - stats->total.Datagrams.prev;
+ stats->cntIPG = stats->total.IPG.current - stats->total.IPG.prev;
+ if (final) {
+ if ((stats->output_handler) && !(stats->isMaskOutput)) {
+ reporter_set_timestamps_time(stats, FINALPARTIAL);
+ if ((stats->ts.iEnd - stats->ts.iStart) > stats->ts.significant_partial)
+ (*stats->output_handler)(stats);
+ }
+ }
+ stats->total.Bytes.prev = stats->total.Bytes.current;
+ stats->total.IPG.prev = stats->total.IPG.current;
+ stats->total.Datagrams.prev = stats->total.Datagrams.current;
+ }
+ if (final) {
+ stats->cntBytes = stats->total.Bytes.current;
+ stats->cntBytes = stats->total.Bytes.current;
+ stats->cntDatagrams = stats->total.Datagrams.current ;
+ stats->cntIPG = stats->total.IPG.current;
+ stats->IPGsum = TimeDifference(stats->ts.packetTime, stats->ts.startTime);
+ reporter_set_timestamps_time(stats, TOTAL);
+ } else {
+ reporter_set_timestamps_time(stats, INTERVAL);
+ }
+ if ((stats->output_handler) && !(stats->isMaskOutput))
+ (*stats->output_handler)(stats);
+ if (stats->cntDatagrams)
+ stats->IPGsum = 0.0;
+}
+
+// Conditional print based on time
+int reporter_condprint_time_interval_report (struct ReporterData *data, struct ReportStruct *packet) {
+ struct TransferInfo *stats = &data->info;
+ assert(stats!=NULL);
+ // printf("***sum handler = %p\n", (void *) data->GroupSumReport->transfer_protocol_sum_handler);
+ int advance_jobq = 0;
+ // Print a report if packet time exceeds the next report interval time,
+ // Also signal to the caller to move to the next report (or packet ring)
+ // if there was output. This will allow for more precise interval sum accounting.
+ // printf("***** pt = %ld.%ld next = %ld.%ld\n", packet->packetTime.tv_sec, packet->packetTime.tv_usec, stats->ts.nextTime.tv_sec, stats->ts.nextTime.tv_usec);
+ // printf("***** nt %ld.%ld pt %ld.%ld pid=%lld empty=%d\n", stats->ts.nextTime.tv_sec, stats->ts.nextTime.tv_usec, packet->packetTime.tv_sec, packet->packetTime.tv_usec, packet->packetID, packet->emptyreport);
+ if (TimeDifference(stats->ts.nextTime, packet->packetTime) < 0) {
+ assert(data->transfer_protocol_handler!=NULL);
+ advance_jobq = 1;
+ struct TransferInfo *sumstats = (data->GroupSumReport ? &data->GroupSumReport->info : NULL);
+ struct TransferInfo *fullduplexstats = (data->FullDuplexReport ? &data->FullDuplexReport->info : NULL);
+ stats->ts.packetTime = packet->packetTime;
+#ifdef DEBUG_PPS
+ printf("*** packetID TRIGGER = %ld pt=%ld.%ld empty=%d nt=%ld.%ld\n",packet->packetID, packet->packetTime.tv_sec, packet->packetTime.tv_usec, packet->emptyreport, stats->ts.nextTime.tv_sec, stats->ts.nextTime.tv_usec);
+#endif
+ reporter_set_timestamps_time(stats, INTERVAL);
+ (*data->transfer_protocol_handler)(data, 0);
+ if (fullduplexstats && ((++data->FullDuplexReport->threads) == 2) && isEnhanced(stats->common)) {
+ data->FullDuplexReport->threads = 0;
+ assert(data->FullDuplexReport->transfer_protocol_sum_handler != NULL);
+ (*data->FullDuplexReport->transfer_protocol_sum_handler)(fullduplexstats, 0);
+ }
+ if (sumstats) {
+ if ((++data->GroupSumReport->threads) == data->GroupSumReport->reference.count) {
+ data->GroupSumReport->threads = 0;
+ if ((data->GroupSumReport->reference.count > (fullduplexstats ? 2 : 1)) || \
+ isSumOnly(data->info.common)) {
+ sumstats->isMaskOutput = false;
+ } else {
+ sumstats->isMaskOutput = true;
+ }
+ reporter_set_timestamps_time(sumstats, INTERVAL);
+ assert(data->GroupSumReport->transfer_protocol_sum_handler != NULL);
+ (*data->GroupSumReport->transfer_protocol_sum_handler)(sumstats, 0);
+ }
+ }
+ // In the (hopefully unlikely event) the reporter fell behind
+ // output the missed reports to catch up
+ if ((stats->output_handler) && !(stats->isMaskOutput))
+ reporter_transfer_protocol_missed_reports(stats, packet);
+ }
+ return advance_jobq;
+}
+
+// Conditional print based on bursts or frames
+int reporter_condprint_frame_interval_report_server_udp (struct ReporterData *data, struct ReportStruct *packet) {
+ struct TransferInfo *stats = &data->info;
+ int advance_jobq = 0;
+ // first packet of a burst and not a duplicate
+ if ((packet->burstsize == (packet->remaining + packet->packetLen)) && (stats->matchframeID != packet->frameID)) {
+ stats->matchframeID=packet->frameID;
+ }
+ if ((packet->packetLen == packet->remaining) && (packet->frameID == stats->matchframeID)) {
+ if ((stats->ts.iStart = TimeDifference(stats->ts.nextTime, stats->ts.startTime)) < 0)
+ stats->ts.iStart = 0.0;
+ stats->frameID = packet->frameID;
+ stats->ts.iEnd = TimeDifference(packet->packetTime, stats->ts.startTime);
+ stats->cntBytes = stats->total.Bytes.current - stats->total.Bytes.prev;
+ stats->cntOutofOrder = stats->total.OutofOrder.current - stats->total.OutofOrder.prev;
+ // assume most of the time out-of-order packets are not
+ // duplicate packets, so conditionally subtract them from the lost packets.
+ stats->cntError = stats->total.Lost.current - stats->total.Lost.prev;
+ stats->cntError -= stats->cntOutofOrder;
+ if (stats->cntError < 0)
+ stats->cntError = 0;
+ stats->cntDatagrams = stats->PacketID - stats->total.Datagrams.prev;
+ if ((stats->output_handler) && !(stats->isMaskOutput))
+ (*stats->output_handler)(stats);
+ reporter_reset_transfer_stats_server_udp(stats);
+ advance_jobq = 1;
+ }
+ return advance_jobq;
+}
+
+int reporter_condprint_frame_interval_report_server_tcp (struct ReporterData *data, struct ReportStruct *packet) {
+ fprintf(stderr, "FIX ME\n");
+ return 1;
+}
+
+int reporter_condprint_burst_interval_report_server_tcp (struct ReporterData *data, struct ReportStruct *packet) {
+ struct TransferInfo *stats = &data->info;
+ int advance_jobq = 0;
+ if (packet->transit_ready) {
+ stats->ts.prevpacketTime = packet->sentTime;
+ stats->ts.packetTime = packet->packetTime;
+ reporter_set_timestamps_time(stats, INTERVALPARTIAL);
+ stats->cntBytes = stats->total.Bytes.current - stats->total.Bytes.prev;
+ if ((stats->output_handler) && !(stats->isMaskOutput))
+ (*stats->output_handler)(stats);
+ reporter_reset_transfer_stats_server_tcp(stats);
+ advance_jobq = 1;
+ }
+ return advance_jobq;
+}
+
+int reporter_condprint_burst_interval_report_client_tcp (struct ReporterData *data, struct ReportStruct *packet) {
+ struct TransferInfo *stats = &data->info;
+ int advance_jobq = 0;
+ // first packet of a burst and not a duplicate
+ if (packet->transit_ready) {
+ reporter_handle_packet_oneway_transit(stats, packet);
+// printf("****sndpkt=%ld.%ld rxpkt=%ld.%ld\n", packet->sentTime.tv_sec, packet->sentTime.tv_usec, packet->packetTime.tv_sec,packet->packetTime.tv_usec);
+ stats->ts.prevpacketTime = packet->prevSentTime;
+ stats->ts.packetTime = packet->packetTime;
+ reporter_set_timestamps_time(stats, INTERVALPARTIAL);
+ stats->cntBytes = stats->total.Bytes.current - stats->total.Bytes.prev;
+ if ((stats->output_handler) && !(stats->isMaskOutput))
+ (*stats->output_handler)(stats);
+ reporter_reset_transfer_stats_client_tcp(stats);
+ advance_jobq = 1;
+ }
+ return advance_jobq;
+}
+
+#ifdef __cplusplus
+} /* end extern "C" */
+#endif
diff --git a/src/Reports.c b/src/Reports.c
new file mode 100644
index 0000000..008f7f4
--- /dev/null
+++ b/src/Reports.c
@@ -0,0 +1,1091 @@
+/*---------------------------------------------------------------
+ * Copyright (c) 1999,2000,2001,2002,2003
+ * The Board of Trustees of the University of Illinois
+ * All Rights Reserved.
+ *---------------------------------------------------------------
+ * 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 CONTRIBUTORS 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.
+ * ________________________________________________________________
+ * National Laboratory for Applied Network Research
+ * National Center for Supercomputing Applications
+ * University of Illinois at Urbana-Champaign
+ * http://www.ncsa.uiuc.edu
+ * ________________________________________________________________
+ *
+ * Reporter.c
+ * rewritten by Robert McMahon
+ * -------------------------------------------------------------------
+ * Handle instantiation and deletion of reports, including sum reports,
+ * in a thread safe way
+ * ------------------------------------------------------------------- */
+
+#include "headers.h"
+#include <math.h>
+#include "Settings.hpp"
+#include "PerfSocket.hpp"
+#include "Reporter.h"
+#include "Locale.h"
+#include "active_hosts.h"
+#include "payloads.h"
+static int transferid_counter = 0;
+
+static inline int my_str_copy(char **dst, char *src) {
+ int cnt = 0;
+ if (src) {
+ *dst = (char *) calloc(strlen(src) + 1, sizeof(char));
+ if (*dst == NULL) {
+ fprintf(stderr, "Out of Memory!!\n");
+ exit(1);
+ }
+ cnt = strlen(src) + 1;
+ strcpy((*dst), src);
+ } else {
+ *dst = NULL;
+ }
+ return cnt;
+}
+
+// These are the thread settings that are shared among report types
+// Make a copy vs referencing the thread setting object. This will
+// better encpasulate report handling.
+static void common_copy (struct ReportCommon **common, struct thread_Settings *inSettings) {
+ // Do deep copies from settings
+ *common = (struct ReportCommon *) calloc(1, sizeof(struct ReportCommon));
+ my_str_copy(&(*common)->Host, inSettings->mHost);
+ my_str_copy(&(*common)->HideHost, inSettings->mHideHost);
+ my_str_copy(&(*common)->Localhost, inSettings->mLocalhost);
+ my_str_copy(&(*common)->Ifrname, inSettings->mIfrname);
+ my_str_copy(&(*common)->Ifrnametx, inSettings->mIfrnametx);
+ my_str_copy(&(*common)->SSMMulticastStr, inSettings->mSSMMulticastStr);
+ my_str_copy(&(*common)->Congestion, inSettings->mCongestion);
+ my_str_copy(&(*common)->transferIDStr, inSettings->mTransferIDStr);
+ my_str_copy(&(*common)->PermitKey, inSettings->mPermitKey);
+
+ // copy some relevant settings
+ (*common)->flags = inSettings->flags;
+ (*common)->flags_extend = inSettings->flags_extend;
+ (*common)->flags_extend2 = inSettings->flags_extend2;
+ (*common)->ThreadMode = inSettings->mThreadMode;
+ (*common)->ReportMode = inSettings->mReportMode;
+ (*common)->KeyCheck = inSettings->mKeyCheck;
+ (*common)->Format = inSettings->mFormat;
+ (*common)->TTL = inSettings->mTTL;
+ // copy some traffic related settings
+ (*common)->BufLen = inSettings->mBufLen;
+ (*common)->MSS = inSettings->mMSS;
+ (*common)->TCPWin = inSettings->mTCPWin;
+ (*common)->FQPacingRate = inSettings->mFQPacingRate;
+ (*common)->Port = inSettings->mPort;
+ (*common)->PortLast = inSettings->mPortLast;
+ (*common)->BindPort = inSettings->mBindPort;
+ (*common)->ListenPort = inSettings->mListenPort;
+ (*common)->AppRate = inSettings->mAppRate;
+ (*common)->BurstSize = inSettings->mBurstSize;
+ (*common)->AppRateUnits = inSettings->mAppRateUnits;
+ (*common)->socket = inSettings->mSock;
+ (*common)->transferID = inSettings->mTransferID;
+ (*common)->threads = inSettings->mThreads;
+ (*common)->winsize_requested = inSettings->mTCPWin;
+#if defined(HAVE_LINUX_FILTER_H) && defined(HAVE_AF_PACKET)
+ (*common)->socketdrop = inSettings->mSockDrop;
+#endif
+ (*common)->peer = inSettings->peer;
+ (*common)->size_peer = inSettings->size_peer;
+ (*common)->jitter_binwidth = inSettings->jitter_binwidth;
+ (*common)->local = inSettings->local;
+ (*common)->size_local = inSettings->size_local;
+ (*common)->HistBins =inSettings->mHistBins;
+ (*common)->HistBinsize =inSettings->mHistBinsize;
+ (*common)->HistUnits =inSettings->mHistUnits;
+ (*common)->pktIPG =inSettings->mBurstIPG;
+ (*common)->rtt_weight = inSettings->rtt_nearcongest_weight_factor;
+ (*common)->ListenerTimeout =inSettings->mListenerTimeout;
+ (*common)->FPS = inSettings->mFPS;
+ (*common)->TOS = inSettings->mTOS;
+ (*common)->RTOS = inSettings->mRTOS;
+ (*common)->bbsize = inSettings->mBounceBackBytes;
+ (*common)->bbhold = inSettings->mBounceBackHold;
+ (*common)->bbcount = inSettings->mBounceBackBurst;
+#if HAVE_DECL_TCP_WINDOW_CLAMP
+ (*common)->ClampSize = inSettings->mClampSize;
+#endif
+#if HAVE_DECL_TCP_NOTSENT_LOWAT
+ (*common)->WritePrefetch = inSettings->mWritePrefetch;
+#endif
+#ifdef HAVE_THREAD_DEBUG
+ thread_debug("Alloc common rpt/com/size %p/%p/%d", (void *) common, (void *)(*common), sizeof(struct ReportCommon));
+#endif
+}
+
+static void free_common_copy (struct ReportCommon *common) {
+ assert(common != NULL);
+#ifdef HAVE_THREAD_DEBUG
+ thread_debug("Free common=%p", (void *)common);
+#endif
+ // Free deep copies
+ if (common->Host)
+ free(common->Host);
+ if (common->HideHost)
+ free(common->HideHost);
+ if (common->Localhost)
+ free(common->Localhost);
+ if (common->Ifrname)
+ free(common->Ifrname);
+ if (common->Ifrnametx)
+ free(common->Ifrnametx);
+ if (common->SSMMulticastStr)
+ free(common->SSMMulticastStr);
+ if (common->Congestion)
+ free(common->Congestion);
+ if (common->transferIDStr)
+ free(common->transferIDStr);
+ if (common->PermitKey)
+ free(common->PermitKey);
+ free(common);
+}
+
+// This will set the transfer id and id string
+// on the setting object. If the current id is zero
+// this will get the next one. Otherwise it will use
+// the value.
+void setTransferID (struct thread_Settings *inSettings, int role_reversal) {
+ if (!inSettings->mTransferIDStr) {
+ if (!inSettings->mTransferID) {
+ Mutex_Lock(&transferid_mutex);
+ inSettings->mTransferID = ++transferid_counter;
+ Mutex_Unlock(&transferid_mutex);
+ }
+ int len = 0;
+ if (role_reversal) {
+#ifdef HAVE_ROLE_REVERSAL_ID
+ if (isPermitKey(inSettings) && (inSettings->mPermitKey[0] != '\0')) {
+ len = snprintf(NULL, 0, "[%s(*%d)] ", \
+ inSettings->mPermitKey, inSettings->mTransferID);
+ inSettings->mTransferIDStr = (char *) calloc(len + 1, sizeof(char));
+ len = sprintf(inSettings->mTransferIDStr, "[%s(*%d)] ", \
+ inSettings->mPermitKey, inSettings->mTransferID);
+ } else if (inSettings->mTransferID < 10) {
+ len = snprintf(NULL, 0, "[ *%d] ", inSettings->mTransferID);
+ inSettings->mTransferIDStr = (char *) calloc(len + 1, sizeof(char));
+ len = sprintf(inSettings->mTransferIDStr, "[ *%d] ", inSettings->mTransferID);
+ } else {
+ len = snprintf(NULL, 0, "[*%d] ", inSettings->mTransferID);
+ inSettings->mTransferIDStr = (char *) calloc(len + 1, sizeof(char));
+ len = sprintf(inSettings->mTransferIDStr, "[*%d] ", inSettings->mTransferID);
+ }
+#endif
+ } else if (isPermitKey(inSettings) && (inSettings->mPermitKey[0] != '\0')) {
+ len = snprintf(NULL, 0, "[%s(%d)] ", \
+ inSettings->mPermitKey, inSettings->mTransferID);
+ inSettings->mTransferIDStr = (char *) calloc(len + 1, sizeof(char));
+ len = sprintf(inSettings->mTransferIDStr, "[%s(%d)] ", \
+ inSettings->mPermitKey, inSettings->mTransferID);
+ } else {
+ len = snprintf(NULL, 0, "[%3d] ", inSettings->mTransferID);
+ inSettings->mTransferIDStr = (char *) calloc(len+1, sizeof(char));
+ len = sprintf(inSettings->mTransferIDStr, "[%3d] ", inSettings->mTransferID);
+ }
+ }
+}
+
+void SetFullDuplexHandlers (struct thread_Settings *inSettings, struct SumReport* sumreport) {
+ if (isUDP(inSettings)) {
+ sumreport->transfer_protocol_sum_handler = reporter_transfer_protocol_fullduplex_udp;
+ sumreport->info.output_handler = ((inSettings->mReportMode == kReport_CSV) ? NULL : \
+ (isSumOnly(inSettings) ? NULL : \
+ (isEnhanced(inSettings) ? udp_output_fullduplex_enhanced : udp_output_fullduplex)));
+ } else {
+ sumreport->transfer_protocol_sum_handler = reporter_transfer_protocol_fullduplex_tcp;
+ sumreport->info.output_handler = ((inSettings->mReportMode == kReport_CSV) ? NULL : \
+ (isSumOnly(inSettings) ? NULL : \
+ (isEnhanced(inSettings) ? tcp_output_fullduplex_enhanced : tcp_output_fullduplex)));
+ }
+}
+
+void SetSumHandlers (struct thread_Settings *inSettings, struct SumReport* sumreport) {
+ switch (inSettings->mThreadMode) {
+ case kMode_Server :
+ if (isUDP(inSettings)) {
+ sumreport->transfer_protocol_sum_handler = reporter_transfer_protocol_sum_server_udp;
+ if (inSettings->mReportMode == kReport_CSV) {
+ if (isEnhanced(inSettings))
+ sumreport->info.output_handler = udp_output_enhanced_csv;
+ else
+ sumreport->info.output_handler = udp_output_basic_csv;
+ } else {
+ if (isTripTime(inSettings)) {
+ sumreport->info.output_handler = udp_output_sumcnt_read_triptime;
+ } else if (isSumOnly(inSettings)) {
+ sumreport->info.output_handler = udp_output_sumcnt_enhanced;
+ } else if (isFullDuplex(inSettings)) {
+ sumreport->info.output_handler = udp_output_fullduplex_sum;
+ } else {
+ sumreport->info.output_handler = (isEnhanced(inSettings) ? udp_output_sum_read_enhanced : udp_output_sum_read);
+ }
+ }
+ } else {
+ sumreport->transfer_protocol_sum_handler = reporter_transfer_protocol_sum_server_tcp;
+ if (inSettings->mReportMode == kReport_CSV) {
+ if (isEnhanced(inSettings))
+ sumreport->info.output_handler = tcp_output_read_enhanced_csv;
+ else
+ sumreport->info.output_handler = tcp_output_basic_csv;
+ } else {
+ if (isTripTime(inSettings)) {
+ sumreport->info.output_handler = tcp_output_sumcnt_read_triptime;
+ } else if (isSumOnly(inSettings)) {
+ sumreport->info.output_handler = (isEnhanced(inSettings) ? tcp_output_sumcnt_read_enhanced : tcp_output_sumcnt_read);
+ } else if (isFullDuplex(inSettings)) {
+ sumreport->info.output_handler = tcp_output_sum_read;
+ } else {
+ sumreport->info.output_handler = (isEnhanced(inSettings) ? tcp_output_sum_read_enhanced : tcp_output_sum_read);
+ }
+ }
+ }
+ break;
+ case kMode_Client :
+ if (isUDP(inSettings)) {
+ sumreport->transfer_protocol_sum_handler = reporter_transfer_protocol_sum_client_udp;
+ if (inSettings->mReportMode == kReport_CSV) {
+ if (isEnhanced(inSettings))
+ sumreport->info.output_handler = udp_output_enhanced_csv;
+ else
+ sumreport->info.output_handler = udp_output_basic_csv;
+ } else {
+ if (isSumOnly(inSettings)) {
+ sumreport->info.output_handler = ((isEnhanced(inSettings) && !isFullDuplex(inSettings)) ? \
+ udp_output_sumcnt_write_enhanced : udp_output_sumcnt);
+ } else if (isFullDuplex(inSettings)) {
+ sumreport->info.output_handler = udp_output_fullduplex_sum;
+ } else {
+ sumreport->info.output_handler = (isEnhanced(inSettings) ? udp_output_sum_write_enhanced : udp_output_sum_write);
+ }
+ }
+ } else {
+ sumreport->transfer_protocol_sum_handler = reporter_transfer_protocol_sum_client_tcp;
+ if (inSettings->mReportMode == kReport_CSV) {
+ if (isEnhanced(inSettings))
+ sumreport->info.output_handler = tcp_output_write_enhanced_csv;
+ else
+ sumreport->info.output_handler = tcp_output_basic_csv;
+ } else {
+ if (isSumOnly(inSettings)) {
+ sumreport->info.output_handler = (isEnhanced(inSettings) ? tcp_output_sumcnt_write_enhanced : tcp_output_sumcnt_write);
+ } else if (isFullDuplex(inSettings)) {
+ sumreport->info.output_handler = tcp_output_fullduplex_sum;
+ } else {
+ sumreport->info.output_handler = (isEnhanced(inSettings) ? tcp_output_sum_write_enhanced : tcp_output_sum_write);
+ }
+ }
+ }
+ break;
+ default:
+ FAIL(1, "SetSumReport", inSettings);
+ }
+}
+
+struct SumReport* InitSumReport(struct thread_Settings *inSettings, int inID, int fullduplex_report) {
+ struct SumReport *sumreport = (struct SumReport *) calloc(1, sizeof(struct SumReport));
+ if (sumreport == NULL) {
+ FAIL(1, "Out of Memory!!\n", inSettings);
+ }
+ sumreport->reference.count = 0;
+ sumreport->reference.maxcount = 0;
+ Mutex_Initialize(&sumreport->reference.lock);
+ sumreport->threads = 0;
+ common_copy(&sumreport->info.common, inSettings);
+ // sumreport->info.common->transferID = inID; // this is now set in the active code
+ sumreport->info.threadcnt = 0;
+ sumreport->info.isMaskOutput = false;
+ if (inSettings->mReportMode == kReport_CSV) {
+ format_ips_port_string(&sumreport->info, 1);
+ }
+
+ // Only initialize the interval time here
+ // The startTime and nextTime for summing reports will be set by
+ // the reporter thread in realtime
+ if ((inSettings->mInterval) && (inSettings->mIntervalMode == kInterval_Time)) {
+ sumreport->info.ts.intervalTime.tv_sec = (long) (inSettings->mInterval / rMillion);
+ sumreport->info.ts.intervalTime.tv_usec = (long) (inSettings->mInterval % rMillion);
+ sumreport->info.ts.significant_partial = ((double) inSettings->mInterval * PARTIALPERCENT / rMillion) ;
+ }
+ // Note that for UDP the client flag settings have not been read (and set) so only use server side flags in tests
+ if (isEnhanced(inSettings) && (inSettings->mThreadMode == kMode_Server) && !fullduplex_report) {
+ if (isHistogram(inSettings)) {
+ if (isUDP(inSettings)) {
+ char name[] = "SUMT8";
+ sumreport->info.latency_histogram = histogram_init(inSettings->mHistBins,inSettings->mHistBinsize,0,\
+ pow(10,inSettings->mHistUnits), \
+ inSettings->mHistci_lower, inSettings->mHistci_upper, sumreport->info.common->transferID, name);
+ } else {
+ char name[] = "SUMF8";
+ sumreport->info.framelatency_histogram = histogram_init(inSettings->mHistBins,inSettings->mHistBinsize,0, \
+ pow(10,inSettings->mHistUnits), inSettings->mHistci_lower, \
+ inSettings->mHistci_upper, sumreport->info.common->transferID, name);
+ }
+ }
+ if (isJitterHistogram(inSettings) && isUDP(inSettings)) {
+ char name[] = "SUMJ8";
+ sumreport->info.jitter_histogram = histogram_init(JITTER_BINCNT,inSettings->jitter_binwidth,0,JITTER_UNITS, \
+ JITTER_LCI, JITTER_UCI, sumreport->info.common->transferID, name);
+ }
+ }
+ if (fullduplex_report) {
+ SetFullDuplexHandlers(inSettings, sumreport);
+ if (!isServerReverse(inSettings)) {
+ sumreport->fullduplex_barrier.count = 0;
+ Condition_Initialize(&sumreport->fullduplex_barrier.await);
+ sumreport->fullduplex_barrier.timeout = ((isModeTime(inSettings) && isUDP(inSettings)) ? \
+ ((int)(inSettings->mAmount / 100) + 1) : MINBARRIERTIMEOUT);
+ if (sumreport->fullduplex_barrier.timeout < MINBARRIERTIMEOUT)
+ sumreport->fullduplex_barrier.timeout = MINBARRIERTIMEOUT;
+ } else {
+ sumreport->info.ts.startTime = inSettings->accept_time;
+ sumreport->info.ts.nextTime = sumreport->info.ts.startTime;
+ TimeAdd(sumreport->info.ts.nextTime, sumreport->info.ts.intervalTime);
+ }
+ } else {
+ SetSumHandlers(inSettings, sumreport);
+ }
+#ifdef HAVE_THREAD_DEBUG
+ thread_debug("Init sum report %p id=%d", (void *)sumreport, inID);
+#endif
+ return sumreport;
+}
+
+struct ConnectionInfo * InitConnectOnlyReport (struct thread_Settings *thread) {
+ assert(thread != NULL);
+ // this connection report used only by report for accumulate stats
+ struct ConnectionInfo *creport = (struct ConnectionInfo *) calloc(1, sizeof(struct ConnectionInfo));
+ if (!creport) {
+ FAIL(1, "Out of Memory!!\n", thread);
+ }
+ common_copy(&creport->common, thread);
+ creport->connect_times.min = FLT_MAX;
+ creport->connect_times.max = FLT_MIN;
+ creport->connect_times.vd = 0;
+ creport->connect_times.m2 = 0;
+ creport->connect_times.mean = 0;
+ creport->txholdbacktime = thread->txholdback_timer;
+ return creport;
+}
+
+void FreeSumReport (struct SumReport *sumreport) {
+ assert(sumreport);
+#ifdef HAVE_THREAD_DEBUG
+ thread_debug("Free sum report hdr=%p", (void *)sumreport);
+#endif
+ Condition_Destroy_Reference(&sumreport->reference);
+ if (sumreport->info.latency_histogram) {
+ histogram_delete(sumreport->info.latency_histogram);
+ }
+ if (sumreport->info.framelatency_histogram) {
+ histogram_delete(sumreport->info.framelatency_histogram);
+ }
+ if (sumreport->info.bbrtt_histogram) {
+ histogram_delete(sumreport->info.bbrtt_histogram);
+ }
+ if (sumreport->info.jitter_histogram) {
+ histogram_delete(sumreport->info.jitter_histogram);
+ }
+ free_common_copy(sumreport->info.common);
+ free(sumreport);
+}
+
+
+static void Free_iReport (struct ReporterData *ireport) {
+ assert(ireport != NULL);
+
+#ifdef HAVE_THREAD_DEBUG
+ thread_debug("Free report hdr=%p reporter thread suspend count=%d packetring=%p histo=%p frame histo=%p", \
+ (void *)ireport, ireport->reporter_thread_suspends, (void *) ireport->packetring, \
+ (void *)ireport->info.latency_histogram, (void *) ireport->info.framelatency_histogram);
+#endif
+ if (ireport->packetring && ireport->info.total.Bytes.current && !(isSingleUDP(ireport->info.common)) && \
+ !TimeZero(ireport->info.ts.intervalTime) && (ireport->reporter_thread_suspends < 3)) {
+ fprintf(stdout, "WARN: this test may have been CPU bound (%d) (or may not be detecting the underlying network devices)\n", \
+ ireport->reporter_thread_suspends);
+ }
+ if (ireport->packetring) {
+ packetring_free(ireport->packetring);
+ }
+ if (ireport->info.latency_histogram) {
+ histogram_delete(ireport->info.latency_histogram);
+ }
+ if (ireport->info.jitter_histogram) {
+ histogram_delete(ireport->info.jitter_histogram);
+ }
+ if (ireport->info.framelatency_histogram) {
+ histogram_delete(ireport->info.framelatency_histogram);
+ }
+ if (ireport->info.bbrtt_histogram) {
+ histogram_delete(ireport->info.bbrtt_histogram);
+ }
+ free_common_copy(ireport->info.common);
+ free(ireport);
+}
+
+void FreeConnectionReport (struct ConnectionInfo *report) {
+ free_common_copy(report->common);
+ free(report);
+}
+
+static void Free_sReport (struct ReportSettings *report) {
+ free_common_copy(report->common);
+ free(report);
+}
+
+static void Free_srReport (struct TransferInfo *report) {
+ free_common_copy(report->common);
+ free(report);
+}
+
+void FreeReport (struct ReportHeader *reporthdr) {
+ assert(reporthdr != NULL);
+#ifdef HAVE_THREAD_DEBUG
+ char rs[REPORTTXTMAX];
+ reporttype_text(reporthdr, &rs[0]);
+ thread_debug("Jobq *FREE* report hdr/rpt %p/%p (%s)", (void *) reporthdr, (void *) reporthdr->this_report, &rs[0]);
+#endif
+ switch (reporthdr->type) {
+ case DATA_REPORT:
+ Free_iReport((struct ReporterData *)reporthdr->this_report);
+ break;
+ case CONNECTION_REPORT:
+ FreeConnectionReport((struct ConnectionInfo *)reporthdr->this_report);
+ break;
+ case SETTINGS_REPORT:
+ Free_sReport((struct ReportSettings *)reporthdr->this_report);
+ break;
+ case SERVER_RELAY_REPORT:
+ Free_srReport((struct TransferInfo *)reporthdr->this_report);
+ break;
+ default:
+ fprintf(stderr, "Invalid report type in free (%x)\n", reporthdr->type);
+ assert(0);
+ break;
+ }
+ free(reporthdr);
+}
+
+/*
+ * InitReport is called by a transfer agent (client or
+ * server) to setup the needed structures to communicate
+ * traffic and connection information. Also initialize
+ * the report start time and next interval report time
+ * Finally, in the case of parallel clients, have them all
+ * synchronize on compeleting their connect()
+ */
+
+void IncrSumReportRefCounter (struct SumReport *sumreport) {
+ assert(sumreport);
+#ifdef HAVE_THREAD_DEBUG
+ thread_debug("Sum multiheader %p ref=%d->%d", (void *)sumreport, sumreport->reference.count, (sumreport->reference.count + 1));
+#endif
+ Mutex_Lock(&sumreport->reference.lock);
+ sumreport->reference.count++;
+ if (sumreport->reference.count > sumreport->reference.maxcount)
+ sumreport->reference.maxcount = sumreport->reference.count;
+ Mutex_Unlock(&sumreport->reference.lock);
+}
+
+int DecrSumReportRefCounter (struct SumReport *sumreport) {
+ assert(sumreport);
+// thread_debug("before lock hdr=%p", (void *)sumreport);
+ Mutex_Lock(&sumreport->reference.lock);
+#ifdef HAVE_THREAD_DEBUG
+ thread_debug("Sum multiheader %p ref=%d->%d", (void *)sumreport, sumreport->reference.count, (sumreport->reference.count - 1));
+#endif
+// thread_debug("in lock hdr=%p", (void *)sumreport);
+ int refcnt = --sumreport->reference.count;
+ Mutex_Unlock(&sumreport->reference.lock);
+// thread_debug("unlock hdr=%p", (void *)sumreport);
+ return refcnt;
+}
+
+
+// Note, this report structure needs to remain self contained and not coupled
+// to any settings structure pointers. This allows the thread settings to
+// be freed without impacting the reporter. It's not recommended that
+// this be done, i.e. free the settings before the report, but be defensive
+// here to allow it
+struct ReportHeader* InitIndividualReport (struct thread_Settings *inSettings) {
+ /*
+ * Create the report header and an ireport (if needed)
+ */
+ assert(inSettings!=NULL);
+ struct ReportHeader *reporthdr = (struct ReportHeader *) calloc(1, sizeof(struct ReportHeader));
+ if (reporthdr == NULL) {
+ FAIL(1, "Out of Memory!!\n", inSettings);
+ }
+ reporthdr->this_report = calloc(1, sizeof(struct ReporterData));
+ if (reporthdr->this_report == NULL) {
+ FAIL(1, "Out of Memory!!\n", inSettings);
+ }
+ reporthdr->type = DATA_REPORT;
+ reporthdr->ReportMode = inSettings->mReportMode;
+
+ struct ReporterData *ireport = (struct ReporterData *)(reporthdr->this_report);
+ if (inSettings->mSumReport) {
+ ireport->GroupSumReport = inSettings->mSumReport;
+ }
+ if (isFullDuplex(inSettings)) {
+ assert(inSettings->mFullDuplexReport != NULL);
+ IncrSumReportRefCounter(inSettings->mFullDuplexReport);
+ ireport->FullDuplexReport = inSettings->mFullDuplexReport;
+ }
+ // Copy common settings into the transfer report section
+ common_copy(&ireport->info.common, inSettings);
+ ireport->info.final = false;
+ ireport->info.burstid_transition = false;
+ ireport->info.isEnableTcpInfo = false;
+
+ // Create a new packet ring which is used to communicate
+ // packet stats from the traffic thread to the reporter
+ // thread. The reporter thread does all packet accounting
+ ireport->packetring = packetring_init((inSettings->numreportstructs ? inSettings->numreportstructs : (isSingleUDP(inSettings) ? 40 : NUM_REPORT_STRUCTS)), \
+ &ReportCond, (isSingleUDP(inSettings) ? NULL : &inSettings->awake_me));
+#ifdef HAVE_THREAD_DEBUG
+ char rs[REPORTTXTMAX];
+ reporttype_text(reporthdr, &rs[0]);
+ thread_debug("Init %s report hdr/rpt/com=%p/%p/%p multireport/fullduplex=%p/%p pring(bytes)/cond=%p(%d)/%p (socket=%d)", &rs[0], \
+ (void *) reporthdr, (void *) ireport, (void *) ireport->info.common, \
+ (void *) inSettings->mSumReport, (void *) inSettings->mFullDuplexReport, \
+ (void *) ireport->packetring, ireport->packetring->bytes, (void *) ireport->packetring->awake_producer, inSettings->mSock);
+#endif
+ if (inSettings->numreportstructs)
+ fprintf (stdout, "%sNUM_REPORT_STRUCTS override from %d to %d\n", inSettings->mTransferIDStr, NUM_REPORT_STRUCTS, inSettings->numreportstructs);
+
+ // Set up the function vectors, there are three
+ // 1) packet_handler: does packet accounting per the test and protocol
+ // 2) transfer_protocol_handler: performs output, e.g. interval reports, per the test and protocol
+
+ if (inSettings->mIntervalMode == kInterval_Time) {
+ ireport->info.ts.intervalTime.tv_sec = (long) (inSettings->mInterval / rMillion);
+ ireport->info.ts.intervalTime.tv_usec = (long) (inSettings->mInterval % rMillion);
+ ireport->transfer_interval_handler = reporter_condprint_time_interval_report;
+ ireport->info.ts.significant_partial = (double) inSettings->mInterval * PARTIALPERCENT / rMillion ;
+ } else {
+ ireport->transfer_interval_handler = NULL;
+ }
+ ireport->packet_handler_pre_report = NULL;
+ ireport->packet_handler_post_report = NULL;
+ switch (inSettings->mThreadMode) {
+ case kMode_Server :
+ if (isUDP(inSettings)) {
+ ireport->packet_handler_post_report = reporter_handle_packet_server_udp;
+ ireport->transfer_protocol_handler = reporter_transfer_protocol_server_udp;
+ if ((inSettings->mIntervalMode == kInterval_Frames) && isIsochronous(inSettings)) {
+ ireport->transfer_interval_handler = reporter_condprint_frame_interval_report_server_udp;
+ ireport->transfer_protocol_handler = reporter_transfer_protocol_server_udp;
+ } else {
+ ireport->transfer_protocol_handler = reporter_transfer_protocol_server_udp;
+ if (isSumOnly(inSettings)) {
+ ireport->info.output_handler = NULL;
+ } else if ((inSettings->mReportMode == kReport_CSV) && !isSumOnly(inSettings)) {
+ if (isEnhanced(inSettings))
+ ireport->info.output_handler = udp_output_enhanced_csv;
+ else
+ ireport->info.output_handler = udp_output_basic_csv;
+ } else if (isTripTime(inSettings)) {
+ if (isIsochronous(inSettings))
+ ireport->info.output_handler = udp_output_read_triptime_isoch;
+ else
+ ireport->info.output_handler = udp_output_read_triptime;
+ } else if (isEnhanced(inSettings)) {
+ ireport->info.output_handler = udp_output_read_enhanced;
+ } else if (isFullDuplex(inSettings)) {
+ ireport->info.output_handler = udp_output_read;
+ } else {
+ ireport->info.output_handler = udp_output_read;
+ }
+ }
+ } else { // TCP case
+ ireport->packet_handler_post_report = reporter_handle_packet_server_tcp;
+ ireport->transfer_protocol_handler = reporter_transfer_protocol_server_tcp;
+ if (isPeriodicBurst(inSettings)) {
+ ireport->transfer_interval_handler = reporter_condprint_burst_interval_report_server_tcp;
+ ireport->info.output_handler = tcp_output_burst_read;
+ ireport->packet_handler_pre_report = reporter_handle_packet_server_tcp;
+ ireport->packet_handler_post_report = NULL;
+ } else if ((inSettings->mIntervalMode == kInterval_Frames) && isIsochronous(inSettings)) {
+ ireport->transfer_interval_handler = reporter_condprint_frame_interval_report_server_tcp;
+ ireport->info.output_handler = tcp_output_frame_read_triptime;
+ ireport->packet_handler_pre_report = reporter_handle_packet_server_tcp;
+ ireport->packet_handler_post_report = NULL;
+ } else if (isSumOnly(inSettings)) {
+ ireport->info.output_handler = NULL;
+ } else if ((inSettings->mReportMode == kReport_CSV) && !isSumOnly(inSettings)) {
+ if (isEnhanced(inSettings))
+ ireport->info.output_handler = tcp_output_read_enhanced_csv;
+ else
+ ireport->info.output_handler = tcp_output_basic_csv;
+ } else if (isBounceBack(inSettings)) {
+ ireport->packet_handler_post_report = reporter_handle_packet_bb_server;
+ ireport->transfer_protocol_handler = reporter_transfer_protocol_server_bb_tcp;
+ ireport->info.output_handler = tcp_output_write;
+ } else if (isTripTime(inSettings) && isIsochronous(inSettings)) {
+ ireport->info.output_handler = tcp_output_read_enhanced_isoch;
+ } else if (isTripTime(inSettings)) {
+ ireport->info.output_handler = tcp_output_read_triptime;
+ } else if (isEnhanced(inSettings)) {
+ ireport->info.output_handler = tcp_output_read_enhanced;
+ } else if (!isFullDuplex(inSettings)) {
+ ireport->info.output_handler = tcp_output_read;
+ } else {
+ ireport->info.output_handler = tcp_output_read;
+ }
+ }
+ break;
+ case kMode_Client :
+ ireport->packet_handler_post_report = reporter_handle_packet_client;
+ if (isUDP(inSettings)) {
+ ireport->transfer_protocol_handler = reporter_transfer_protocol_client_udp;
+ if (isSumOnly(inSettings)) {
+ ireport->info.output_handler = NULL;
+ } else if ((inSettings->mReportMode == kReport_CSV) && !isSumOnly(inSettings)) {
+ if (isEnhanced(inSettings))
+ ireport->info.output_handler = udp_output_enhanced_csv;
+ else
+ ireport->info.output_handler = udp_output_basic_csv;
+ } else if (isIsochronous(inSettings)) {
+ ireport->info.output_handler = udp_output_write_enhanced_isoch;
+ } else if (isEnhanced(inSettings)) {
+ ireport->info.output_handler = udp_output_write_enhanced;
+ } else if (isFullDuplex(inSettings)) {
+ ireport->info.output_handler = udp_output_write;
+ } else {
+ ireport->info.output_handler = udp_output_write;
+ }
+ } else {
+ ireport->transfer_protocol_handler = reporter_transfer_protocol_client_tcp;
+ if (isSumOnly(inSettings)) {
+ ireport->info.output_handler = NULL;
+ } else if ((inSettings->mReportMode == kReport_CSV) && !isSumOnly(inSettings)) {
+ if (isEnhanced(inSettings))
+ ireport->info.output_handler = tcp_output_write_enhanced_csv;
+ else
+ ireport->info.output_handler = tcp_output_basic_csv;
+ } else if (isBounceBack(inSettings)) {
+ ireport->packet_handler_post_report = reporter_handle_packet_bb_client;
+ ireport->transfer_protocol_handler = reporter_transfer_protocol_client_bb_tcp;
+ ireport->info.output_handler = tcp_output_write_bb;
+ } else if (isIsochronous(inSettings)) {
+ ireport->info.output_handler = tcp_output_write_enhanced_isoch;
+ } else if (isTcpWriteTimes(inSettings)) {
+ ireport->info.output_handler = tcp_output_write_enhanced_write;
+ } else if (isEnhanced(inSettings)) {
+ ireport->info.output_handler = tcp_output_write_enhanced;
+ } else if (isFullDuplex(inSettings)) {
+ ireport->info.output_handler = tcp_output_write;
+ } else {
+ ireport->info.output_handler = tcp_output_write;
+ }
+ }
+ break;
+ case kMode_Unknown :
+ case kMode_Reporter :
+ case kMode_ReporterClient :
+ case kMode_Listener:
+ default:
+ FAIL(1, "InitIndividualReport\n", inSettings);
+ }
+
+ if (inSettings->mThreadMode == kMode_Server) {
+ ireport->info.sock_callstats.read.binsize = inSettings->mBufLen / 8;
+ if (isUDP(inSettings)) {
+ if (isJitterHistogram(inSettings)) {
+ char name[] = "J8";
+ ireport->info.jitter_histogram = histogram_init(JITTER_BINCNT,inSettings->jitter_binwidth,0,JITTER_UNITS, \
+ JITTER_LCI, JITTER_UCI, ireport->info.common->transferID, name);
+ }
+ if (isTripTime(inSettings) && isHistogram(inSettings)) {
+ char name[] = "T8";
+ ireport->info.latency_histogram = histogram_init(inSettings->mHistBins,inSettings->mHistBinsize,0,\
+ pow(10,inSettings->mHistUnits), \
+ inSettings->mHistci_lower, inSettings->mHistci_upper, ireport->info.common->transferID, name);
+ }
+ }
+ if (isHistogram(inSettings) && (isIsochronous(inSettings) || (!isUDP(inSettings) && isTripTime(inSettings)))) {
+ char name[] = "F8";
+ // make sure frame bin size min is 100 microsecond
+ ireport->info.framelatency_histogram = histogram_init(inSettings->mHistBins,inSettings->mHistBinsize,0, \
+ pow(10,inSettings->mHistUnits), inSettings->mHistci_lower, \
+ inSettings->mHistci_upper, ireport->info.common->transferID, name);
+ }
+ }
+ if ((inSettings->mThreadMode == kMode_Client) && !isUDP(inSettings) && isHistogram(inSettings)) {
+ if (isTcpWriteTimes(inSettings)) {
+ char name[] = "W8";
+ ireport->info.write_histogram = histogram_init(inSettings->mHistBins,inSettings->mHistBinsize,0,\
+ pow(10,inSettings->mHistUnits), \
+ inSettings->mHistci_lower, inSettings->mHistci_upper, ireport->info.common->transferID, name);
+ } else if (isWritePrefetch(inSettings)) {
+ char name[] = "S8";
+ ireport->info.latency_histogram = histogram_init(inSettings->mHistBins,inSettings->mHistBinsize,0,\
+ pow(10,inSettings->mHistUnits), \
+ inSettings->mHistci_lower, inSettings->mHistci_upper, ireport->info.common->transferID, name);
+ }
+ }
+ if ((inSettings->mThreadMode == kMode_Client) && isBounceBack(inSettings)) {
+ char name[] = "BB8";
+ if (!isHistogram(inSettings)) {
+ inSettings->mHistBins = 100000; // 10 seconds wide
+ inSettings->mHistBinsize = 100; // 100 usec bins
+ inSettings->mHistUnits = 6; // usecs 10 pow(x)
+ inSettings->mHistci_lower = 5;
+ inSettings->mHistci_upper = 95;
+ }
+ ireport->info.bbrtt_histogram = histogram_init(inSettings->mHistBins,inSettings->mHistBinsize,0, \
+ pow(10,inSettings->mHistUnits), \
+ inSettings->mHistci_lower, inSettings->mHistci_upper, ireport->info.common->transferID, name);
+ }
+ return reporthdr;
+}
+
+
+/*
+ * This init/update and print/finish (in the ReportDefault.c)
+ * is poor. It has to be done this way to preserve the
+ * interface to older versions where the reporter settings
+ * were delayed until a Transfer report came through.
+ * This transfer report has all the reports bound to it.
+ *
+ * The better implmementation is to treat all reports
+ * as independent objects that can be updated, processed,
+ * and output independlty per the Reporter threads job queue
+ * without shared state or copied state variables between these
+ * reports. The shared state, is really reporter state, that
+ * should be maintained in and by the reporter object/thread.
+ *
+ * For now, just fix it good enough. Later, write a c++
+ * reporter object and use standard c++ design techniques
+ * to achieve this. Such code will be easier to maintain
+ * and to extend.
+ */
+struct ReportHeader* InitConnectionReport (struct thread_Settings *inSettings) {
+ assert(inSettings != NULL);
+ struct ReportHeader *reporthdr = (struct ReportHeader *) calloc(1, sizeof(struct ReportHeader));
+ if (reporthdr == NULL) {
+ FAIL(1, "Out of Memory!!\n", inSettings);
+ }
+ reporthdr->this_report = calloc(1, sizeof(struct ConnectionInfo));
+ if (reporthdr->this_report == NULL) {
+ FAIL(1, "Out of Memory!!\n", inSettings);
+ }
+ reporthdr->type = CONNECTION_REPORT;
+ reporthdr->ReportMode = inSettings->mReportMode;
+
+ struct ConnectionInfo * creport = (struct ConnectionInfo *)(reporthdr->this_report);
+ common_copy(&creport->common, inSettings);
+ tcpstats_copy(&creport->tcpinitstats, &inSettings->tcpinitstats);
+ // Fill out known fields for the connection report
+ reporter_peerversion(creport, inSettings->peer_version_u, inSettings->peer_version_l);
+ if (isEnhanced(inSettings) && isTxStartTime(inSettings)) {
+ creport->epochStartTime.tv_sec = inSettings->txstart_epoch.tv_sec;
+ creport->epochStartTime.tv_usec = inSettings->txstart_epoch.tv_usec;
+ } else if (isTripTime(inSettings)) {
+ creport->epochStartTime.tv_sec = inSettings->accept_time.tv_sec;
+ creport->epochStartTime.tv_usec = inSettings->accept_time.tv_usec;
+ }
+ // Copy state from the settings object into the connection report
+ creport->connect_times.min = FLT_MAX;
+ creport->connect_times.max = FLT_MIN;
+ creport->connect_times.vd = 0;
+ creport->connect_times.m2 = 0;
+ creport->connect_times.mean = 0;
+ if (inSettings->mSock > 0) {
+ creport->winsize = getsock_tcp_windowsize(inSettings->mSock, \
+ (inSettings->mThreadMode != kMode_Client ? 0 : 1) );
+#if HAVE_DECL_TCP_WINDOW_CLAMP
+ if (isRxClamp(inSettings)) {
+ getsock_tcp_windowclamp(inSettings->mSock);
+ }
+#endif
+ }
+ creport->common->winsize_requested = inSettings->mTCPWin;
+ creport->txholdbacktime = inSettings->txholdback_timer;
+ if (isPeriodicBurst(inSettings)) {
+ creport->common->FPS = inSettings->mFPS;
+ }
+#ifdef HAVE_THREAD_DEBUG
+ char rs[REPORTTXTMAX];
+ reporttype_text(reporthdr, &rs[0]);
+ thread_debug("Init %s report hdr/rpt/com %p/%p/%p", &rs[0], \
+ (void *) reporthdr, (void *) reporthdr->this_report, (void *) creport->common);
+#endif
+ return reporthdr;
+}
+
+/*
+ * ReportSettings will generate a summary report for
+ * settings being used with Listeners or Clients
+ */
+struct ReportHeader *InitSettingsReport (struct thread_Settings *inSettings) {
+ assert(inSettings != NULL);
+ struct ReportHeader *reporthdr = (struct ReportHeader *) calloc(1, sizeof(struct ReportHeader));
+ if (reporthdr == NULL) {
+ FAIL(1, "Out of Memory!!\n", inSettings);
+ }
+ reporthdr->this_report = calloc(1, sizeof(struct ReportSettings));
+ if (reporthdr->this_report == NULL) {
+ FAIL(1, "Out of Memory!!\n", inSettings);
+ }
+ reporthdr->type = SETTINGS_REPORT;
+ reporthdr->ReportMode = inSettings->mReportMode;
+
+ struct ReportSettings *sreport = (struct ReportSettings *)reporthdr->this_report;
+ common_copy(&sreport->common, inSettings);
+ sreport->peer = inSettings->peer;
+ sreport->size_peer = inSettings->size_peer;
+ sreport->local = inSettings->local;
+ sreport->size_local = inSettings->size_local;
+ sreport->isochstats.mFPS = inSettings->mFPS;
+ sreport->isochstats.mMean = inSettings->mMean/8;
+ sreport->isochstats.mVariance = inSettings->mVariance/8;
+ sreport->isochstats.mBurstIPG = (unsigned int) (inSettings->mBurstIPG*1000.0);
+ sreport->isochstats.mBurstInterval = (unsigned int) (1 / inSettings->mFPS * 1000000);
+ if (!isUDP(inSettings)) {
+ if (inSettings->mMSS > 0) {
+ sreport->sockmaxseg = inSettings->mMSS;
+ } else if (isPrintMSS(inSettings) && !(inSettings->mMSS > 0)) {
+ sreport->sockmaxseg = getsock_tcp_mss(inSettings->mSock);
+ }
+ }
+#ifdef HAVE_THREAD_DEBUG
+ char rs[REPORTTXTMAX];
+ reporttype_text(reporthdr, &rs[0]);
+ thread_debug("Init %s report hdr/rpt/com %p/%p/%p", &rs[0], \
+ (void *) reporthdr, (void *) reporthdr->this_report, (void *) sreport->common);
+#endif
+ return reporthdr;
+}
+
+/*
+ * This will generate a report of the UDP
+ * statistics as reported by the server on the client
+ * side.
+ */
+struct ReportHeader* InitServerRelayUDPReport(struct thread_Settings *inSettings, struct server_hdr *server) {
+ /*
+ * Create the report header and an ireport (if needed)
+ */
+ struct ReportHeader *reporthdr = (struct ReportHeader *) calloc(1, sizeof(struct ReportHeader));
+ if (reporthdr == NULL) {
+ FAIL(1, "Out of Memory!!\n", inSettings);
+ }
+ reporthdr->this_report = calloc(1, sizeof(struct ServerRelay));
+ if (!reporthdr->this_report) {
+ FAIL(1, "Out of Memory!!\n", inSettings);
+ }
+#ifdef HAVE_THREAD_DEBUG
+ thread_debug("Init server relay report %p size %ld", (void *)reporthdr, sizeof(struct ReportHeader) + sizeof(struct ServerRelay));
+#endif
+ reporthdr->type = SERVER_RELAY_REPORT;
+ reporthdr->ReportMode = inSettings->mReportMode;
+ struct ServerRelay *sr_report = (struct ServerRelay *)reporthdr->this_report;
+ common_copy(&sr_report->info.common, inSettings);
+ struct TransferInfo *stats = &sr_report->info;
+ stats->common->transferID = inSettings->mTransferID;
+
+ stats->jitter = ntohl(server->base.jitter1);
+ stats->jitter += ntohl(server->base.jitter2) / (double)rMillion;
+#ifdef HAVE_INT64_T
+ stats->cntBytes = (((intmax_t) ntohl(server->base.total_len1)) << 32) + \
+ ntohl(server->base.total_len2);
+#else
+ stats->cntBytes = (intmax_t) ntohl(server->base.total_len2);
+#endif
+ stats->ts.iStart = 0;
+ stats->ts.iEnd = ntohl(server->base.stop_sec);
+ stats->ts.iEnd += ntohl(server->base.stop_usec) / (double)rMillion;
+ uint32_t flags = ntohl(server->base.flags);
+ if ((flags & HEADER_SEQNO64B)) {
+ stats->cntError = (((intmax_t) ntohl(server->extend2.error_cnt2)) << 32) + \
+ ntohl(server->base.error_cnt);
+ stats->cntOutofOrder = (((intmax_t) ntohl(server->extend2.outorder_cnt2)) << 32) + \
+ ntohl(server->base.outorder_cnt);
+ stats->cntDatagrams = (((intmax_t) ntohl(server->extend2.datagrams2)) << 32) + \
+ ntohl(server->base.datagrams);
+ } else {
+ stats->cntError = ntohl(server->base.error_cnt);
+ stats->cntOutofOrder = ntohl(server->base.outorder_cnt);
+ stats->cntDatagrams = ntohl(server->base.datagrams);
+ }
+ if ((flags & SERVER_HEADER_EXTEND) != 0) {
+ setEnhanced(stats->common);
+ stats->transit.current.min = ntohl(server->extend.minTransit1);
+ stats->transit.current.min += ntohl(server->extend.minTransit2) / (double)rMillion;
+ stats->transit.current.max = ntohl(server->extend.maxTransit1);
+ stats->transit.current.max += ntohl(server->extend.maxTransit2) / (double)rMillion;
+ stats->transit.current.sum = ntohl(server->extend.sumTransit1);
+ stats->transit.current.sum += ntohl(server->extend.sumTransit2) / (double)rMillion;
+ stats->transit.current.mean = ntohl(server->extend.meanTransit1);
+ stats->transit.current.mean += ntohl(server->extend.meanTransit2) / (double)rMillion;
+ stats->transit.current.m2 = ntohl(server->extend.m2Transit1);
+ stats->transit.current.m2 += ntohl(server->extend.m2Transit2) / (double)rMillion;
+ stats->transit.current.m2 *= 1e-12;
+ stats->transit.current.vd = ntohl(server->extend.vdTransit1);
+ stats->transit.current.vd += ntohl(server->extend.vdTransit2) / (double)rMillion;
+ stats->transit.current.cnt = ntohl(server->extend.cntTransit);
+ stats->cntIPG = ntohl(server->extend.cntIPG);
+ stats->IPGsum = ntohl(server->extend.IPGsum);
+ } else {
+ unsetEnhanced(stats->common);
+ }
+ sr_report->peer = inSettings->local;
+ sr_report->size_peer = inSettings->size_local;
+ sr_report->local = inSettings->peer;
+ sr_report->size_local = inSettings->size_peer;
+ return reporthdr;
+}
+
+/* -------------------------------------------------------------------
+ * Send an AckFIN (a datagram acknowledging a FIN) on the socket,
+ * then select on the socket for some time to check for silence.
+ * If additional datagrams come in (not silent), probably our AckFIN
+ * was lost so the client has re-transmitted
+ * termination datagrams, so re-transmit our AckFIN.
+ * Sent by server to client
+ * ------------------------------------------------------------------- */
+void write_UDP_AckFIN (struct TransferInfo *stats, int len) {
+ assert(stats!= NULL);
+ int ackpacket_length = (int) (sizeof(struct UDP_datagram) + sizeof(struct server_hdr));
+ int readlen = ((ackpacket_length * 2) > len * 2) ? (ackpacket_length * 2) : (len * 2);
+ char *ackPacket = (char *) calloc(1, readlen);
+ int success = 0;
+ assert(ackPacket);
+ fd_set readSet;
+ int rc = 1;
+ struct timeval timeout;
+
+ if (ackPacket) {
+ struct UDP_datagram *UDP_Hdr = (struct UDP_datagram *)ackPacket;
+ struct server_hdr *hdr = (struct server_hdr *)(UDP_Hdr+1);
+
+ UDP_Hdr = (struct UDP_datagram*) ackPacket;
+ int flags = HEADER_VERSION1;
+ if (isEnhanced(stats->common) || isTripTime(stats->common))
+ flags |= SERVER_HEADER_EXTEND;
+#ifdef HAVE_INT64_T
+ flags |= HEADER_SEQNO64B;
+#endif
+ hdr->base.flags = htonl((long) flags);
+#ifdef HAVE_INT64_T
+ hdr->base.total_len1 = htonl((long) (stats->cntBytes >> 32));
+#else
+ hdr->base.total_len1 = htonl(0x0);
+#endif
+ hdr->base.total_len2 = htonl((long) (stats->cntBytes & 0xFFFFFFFF));
+ hdr->base.stop_sec = htonl( (long) stats->ts.iEnd);
+ hdr->base.stop_usec = htonl( (long)((stats->ts.iEnd - (long)stats->ts.iEnd) * rMillion));
+ hdr->base.error_cnt = htonl((long) (stats->cntError & 0xFFFFFFFF));
+ hdr->base.outorder_cnt = htonl((long) (stats->cntOutofOrder & 0xFFFFFFFF));
+ hdr->base.datagrams = htonl((long) (stats->cntDatagrams & 0xFFFFFFFF));
+ if (flags & HEADER_SEQNO64B) {
+ hdr->extend2.error_cnt2 = htonl((long) (stats->cntError >> 32));
+ hdr->extend2.outorder_cnt2 = htonl((long) (stats->cntOutofOrder >> 32) );
+ hdr->extend2.datagrams2 = htonl((long) (stats->cntDatagrams >> 32));
+ }
+ // printf("****** Server final estimator %f calculated average %f\n", stats->jitter, (stats->inline_jitter.total.sum / stats->inline_jitter.total.cnt));
+ if (stats->inline_jitter.total.cnt > 0)
+ stats->jitter = (stats->inline_jitter.total.sum / stats->inline_jitter.total.cnt); // overide the final estimator with an average
+ hdr->base.jitter1 = htonl((long) stats->jitter);
+ hdr->base.jitter2 = htonl((long) ((stats->jitter - (long)stats->jitter) * rMillion));
+
+ hdr->extend.minTransit1 = htonl((long) stats->transit.total.min);
+ hdr->extend.minTransit2 = htonl((long) ((stats->transit.total.min - (long)stats->transit.total.min) * rMillion));
+ hdr->extend.maxTransit1 = htonl((long) stats->transit.total.max);
+ hdr->extend.maxTransit2 = htonl((long) ((stats->transit.total.max - (long)stats->transit.total.max) * rMillion));
+ hdr->extend.sumTransit1 = htonl((long) stats->transit.total.sum);
+ hdr->extend.sumTransit2 = htonl((long) ((stats->transit.total.sum - (long)stats->transit.total.sum) * rMillion));
+ hdr->extend.meanTransit1 = htonl((long) stats->transit.total.mean);
+ hdr->extend.meanTransit2 = htonl((long) ((stats->transit.total.mean - (long)stats->transit.total.mean) * rMillion));
+ stats->transit.total.m2 *= 1e12;
+ hdr->extend.m2Transit1 = htonl((long) stats->transit.total.m2);
+ hdr->extend.m2Transit2 = htonl((long) ((stats->transit.total.m2 - (long)stats->transit.total.m2) * rMillion));
+ hdr->extend.vdTransit1 = htonl((long) stats->transit.total.vd);
+ hdr->extend.vdTransit2 = htonl((long) ((stats->transit.total.vd - (long)stats->transit.total.vd) * rMillion));
+ hdr->extend.cntTransit = htonl(stats->transit.total.cnt);
+ hdr->extend.cntIPG = htonl((long) (stats->cntDatagrams / (stats->ts.iEnd - stats->ts.iStart)));
+ hdr->extend.IPGsum = htonl(1);
+
+#define TRYCOUNT 10
+ int count = TRYCOUNT;
+ while (--count) {
+ // write data
+#if defined(HAVE_LINUX_FILTER_H) && defined(HAVE_AF_PACKET)
+ // If in l2mode, use the AF_INET socket to write this packet
+ //
+#ifdef HAVE_THREAD_DEBUG
+ thread_debug("UDP server send done-ack w/server-stats to client (sock=%d)", stats->common->socket);
+#endif
+ rc = write(((stats->common->socketdrop > 0) ? stats->common->socketdrop : stats->common->socket), ackPacket, ackpacket_length);
+#else
+ rc = write(stats->common->socket, ackPacket, ackpacket_length);
+#endif
+ WARN_errno(rc < 0, "write-ackfin");
+ // wait here is for silence, no more packets from the client
+
+ FD_ZERO(&readSet);
+ FD_SET(stats->common->socket, &readSet);
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 250000;
+ rc = select(stats->common->socket+1, &readSet, NULL, NULL, &timeout);
+ if (rc == 0) {
+#ifdef HAVE_THREAD_DEBUG
+ thread_debug("UDP server detected silence - server stats assumed received by client");
+#endif
+ success = 1;
+ break;
+ }
+ rc = read(stats->common->socket, ackPacket, readlen);
+ // WARN_errno(rc < 0, "ack await silence");
+ if ((rc < 0) && FATALUDPREADERR(errno)) {
+ break;
+ }
+#ifdef HAVE_THREAD_DEBUG
+ if (rc > 0) {
+ thread_debug("UDP server thinks server stats packet maybe lost, will retransmit and try again", rc);
+ }
+#endif
+ }
+ free(ackPacket);
+ }
+ if (!success && (stats->common->ReportMode != kReport_CSV)) {
+ fprintf(stderr, warn_ack_failed, stats->common->socket);
+ }
+}
+// end write_UDP_AckFIN
diff --git a/src/Server.cpp b/src/Server.cpp
new file mode 100644
index 0000000..c271cd7
--- /dev/null
+++ b/src/Server.cpp
@@ -0,0 +1,946 @@
+/*---------------------------------------------------------------
+ * Copyright (c) 1999,2000,2001,2002,2003
+ * The Board of Trustees of the University of Illinois
+ * All Rights Reserved.
+ *---------------------------------------------------------------
+ * 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.
+ * ________________________________________________________________
+ * National Laboratory for Applied Network Research
+ * National Center for Supercomputing Applications
+ * University of Illinois at Urbana-Champaign
+ * http://www.ncsa.uiuc.edu
+ * ________________________________________________________________
+ *
+ * Server.cpp
+ * by Mark Gates <mgates@nlanr.net>
+ * Ajay Tirumala (tirumala@ncsa.uiuc.edu>.
+ * -------------------------------------------------------------------
+ * A server thread is initiated for each connection accept() returns.
+ * Handles sending and receiving data, and then closes socket.
+ * Changes to this version : The server can be run as a daemon
+ * ------------------------------------------------------------------- */
+
+#define HEADERS()
+
+#include "headers.h"
+#include "Server.hpp"
+#include "active_hosts.h"
+#include "Extractor.h"
+#include "Reporter.h"
+#include "Locale.h"
+#include "delay.h"
+#include "PerfSocket.hpp"
+#include "SocketAddr.h"
+#include "payloads.h"
+#include <cmath>
+#if defined(HAVE_LINUX_FILTER_H) && defined(HAVE_AF_PACKET)
+#include "checksums.h"
+#endif
+
+
+/* -------------------------------------------------------------------
+ * Stores connected socket and socket info.
+ * ------------------------------------------------------------------- */
+
+Server::Server (thread_Settings *inSettings) {
+#ifdef HAVE_THREAD_DEBUG
+ thread_debug("Server constructor with thread=%p sum=%p (sock=%d)", (void *) inSettings, (void *)inSettings->mSumReport, inSettings->mSock);
+#endif
+ mSettings = inSettings;
+ myJob = NULL;
+ reportstruct = &scratchpad;
+ memset(&scratchpad, 0, sizeof(struct ReportStruct));
+ mySocket = inSettings->mSock;
+ peerclose = false;
+#if defined(HAVE_LINUX_FILTER_H) && defined(HAVE_AF_PACKET)
+ myDropSocket = inSettings->mSockDrop;
+ if (isL2LengthCheck(mSettings)) {
+ // For L2 UDP make sure we can receive a full ethernet packet plus a bit more
+ if (mSettings->mBufLen < (2 * ETHER_MAX_LEN)) {
+ mSettings->mBufLen = (2 * ETHER_MAX_LEN);
+ }
+ }
+#endif
+ // Enable kernel level timestamping if available
+ InitKernelTimeStamping();
+ int sorcvtimer = 0;
+ // sorcvtimer units microseconds convert to that
+ // minterval double, units seconds
+ // mAmount integer, units 10 milliseconds
+ // divide by two so timeout is 1/2 the interval
+ if (mSettings->mInterval && (mSettings->mIntervalMode == kInterval_Time)) {
+ sorcvtimer = static_cast<int>(round(mSettings->mInterval / 2.0));
+ } else if (isServerModeTime(mSettings)) {
+ sorcvtimer = static_cast<int>(round(mSettings->mAmount * 10000) / 2);
+ }
+ isburst = (isIsochronous(mSettings) || isPeriodicBurst(mSettings) || (isTripTime(mSettings) && !isUDP(mSettings)));
+ if (isburst && (mSettings->mFPS > 0.0)) {
+ sorcvtimer = static_cast<int>(round(2000000.0 / mSettings->mFPS));
+ }
+ if (sorcvtimer > 0) {
+ SetSocketOptionsReceiveTimeout(mSettings, sorcvtimer);
+ }
+}
+
+/* -------------------------------------------------------------------
+ * Destructor close socket.
+ * ------------------------------------------------------------------- */
+Server::~Server () {
+#if HAVE_THREAD_DEBUG
+ thread_debug("Server destructor sock=%d fullduplex=%s", mySocket, (isFullDuplex(mSettings) ? "true" : "false"));
+#endif
+#if defined(HAVE_LINUX_FILTER_H) && defined(HAVE_AF_PACKET)
+ if (myDropSocket != INVALID_SOCKET) {
+ int rc = close(myDropSocket);
+ WARN_errno(rc == SOCKET_ERROR, "server close drop");
+ myDropSocket = INVALID_SOCKET;
+ }
+#endif
+}
+
+inline bool Server::InProgress () {
+ return !(sInterupted || peerclose ||
+ ((isServerModeTime(mSettings) || (isModeTime(mSettings) && isReverse(mSettings))) && mEndTime.before(reportstruct->packetTime)));
+}
+
+/* -------------------------------------------------------------------
+ * Receive TCP data from the (connected) socket.
+ * Sends termination flag several times at the end.
+ * Does not close the socket.
+ * ------------------------------------------------------------------- */
+void Server::RunTCP () {
+ long currLen;
+ intmax_t totLen = 0;
+ struct TCP_burst_payload burst_info; // used to store burst header and report in last packet of burst
+ Timestamp time1, time2;
+ double tokens=0.000004;
+
+ if (!InitTrafficLoop())
+ return;
+ myReport->info.ts.prevsendTime = myReport->info.ts.startTime;
+
+ int burst_nleft = 0;
+ burst_info.burst_id = 0;
+ burst_info.burst_period_us = 0;
+ burst_info.send_tt.write_tv_sec = 0;
+ burst_info.send_tt.write_tv_usec = 0;
+ now.setnow();
+ reportstruct->packetTime.tv_sec = now.getSecs();
+ reportstruct->packetTime.tv_usec = now.getUsecs();
+ while (InProgress()) {
+// printf("***** bid expect = %u\n", burstid_expect);
+ reportstruct->emptyreport=1;
+ currLen = 0;
+ // perform read
+ if (isBWSet(mSettings)) {
+ time2.setnow();
+ tokens += time2.subSec(time1) * (mSettings->mAppRate / 8.0);
+ time1 = time2;
+ }
+ reportstruct->transit_ready = 0;
+ if (tokens >= 0.0) {
+ int n = 0;
+ int readLen = mSettings->mBufLen;
+ if (burst_nleft > 0)
+ readLen = (mSettings->mBufLen < burst_nleft) ? mSettings->mBufLen : burst_nleft;
+ reportstruct->emptyreport=1;
+#if HAVE_DECL_TCP_QUICKACK
+ if (isTcpQuickAck(mSettings)) {
+ int opt = 1;
+ Socklen_t len = sizeof(opt);
+ int rc = setsockopt(mySocket, IPPROTO_TCP, TCP_QUICKACK,
+ reinterpret_cast<char*>(&opt), len);
+ WARN_errno(rc == SOCKET_ERROR, "setsockopt TCP_QUICKACK");
+ }
+#endif
+ if (isburst && (burst_nleft == 0)) {
+ if ((n = recvn(mSettings->mSock, reinterpret_cast<char *>(&burst_info), sizeof(struct TCP_burst_payload), 0)) == sizeof(struct TCP_burst_payload)) {
+ // burst_info.typelen.type = ntohl(burst_info.typelen.type);
+ // burst_info.typelen.length = ntohl(burst_info.typelen.length);
+ // This is the first stamp of the burst
+ burst_info.flags = ntohl(burst_info.flags);
+ burst_info.burst_size = ntohl(burst_info.burst_size);
+ assert(burst_info.burst_size > 0);
+ reportstruct->burstsize = burst_info.burst_size;
+ burst_info.burst_id = ntohl(burst_info.burst_id);
+ reportstruct->frameID = burst_info.burst_id;
+ if (isTripTime(mSettings)) {
+ burst_info.send_tt.write_tv_sec = ntohl(burst_info.send_tt.write_tv_sec);
+ burst_info.send_tt.write_tv_usec = ntohl(burst_info.send_tt.write_tv_usec);
+ } else if (isIsochronous(mSettings)) {
+ burst_info.send_tt.write_tv_sec = (uint32_t)myReport->info.ts.startTime.tv_sec;
+ burst_info.send_tt.write_tv_usec = (uint32_t)myReport->info.ts.startTime.tv_usec;
+ burst_info.burst_period_us = ntohl(burst_info.burst_period_us);
+ } else {
+ now.setnow();
+ burst_info.send_tt.write_tv_sec = (uint32_t)now.getSecs();
+ burst_info.send_tt.write_tv_usec = (uint32_t)now.getUsecs();
+ }
+ reportstruct->sentTime.tv_sec = burst_info.send_tt.write_tv_sec;
+ reportstruct->sentTime.tv_usec = burst_info.send_tt.write_tv_usec;
+ myReport->info.ts.prevsendTime = reportstruct->sentTime;
+ burst_nleft = burst_info.burst_size - n;
+ if (burst_nleft == 0) {
+ reportstruct->prevSentTime = myReport->info.ts.prevsendTime;
+ reportstruct->transit_ready = 1;
+ reportstruct->burstperiod = burst_info.burst_period_us;
+ }
+ currLen += n;
+ readLen = (mSettings->mBufLen < burst_nleft) ? mSettings->mBufLen : burst_nleft;
+ WARN(burst_nleft <= 0, "invalid burst read req size");
+ // thread_debug("***read burst header size %d id=%d", burst_info.burst_size, burst_info.burst_id);
+ } else {
+ if (n > 0) {
+ WARN(1, "partial readn");
+#ifdef HAVE_THREAD_DEBUG
+ thread_debug("TCP burst partial read of %d wanted %d", n, sizeof(struct TCP_burst_payload));
+ } else {
+ thread_debug("Detected peer close");
+#endif
+ }
+ goto Done;
+ }
+ }
+ if (!reportstruct->transit_ready) {
+ n = recv(mSettings->mSock, mSettings->mBuf, readLen, 0);
+ if (n > 0) {
+ reportstruct->emptyreport = 0;
+ if (isburst) {
+ burst_nleft -= n;
+ if (burst_nleft == 0) {
+ reportstruct->sentTime = myReport->info.ts.prevsendTime;
+ if (isTripTime(mSettings) || isIsochronous(mSettings)) {
+ reportstruct->isochStartTime.tv_sec = burst_info.send_tt.write_tv_sec;
+ reportstruct->isochStartTime.tv_usec = burst_info.send_tt.write_tv_usec;
+ reportstruct->burstperiod = burst_info.burst_period_us;
+ }
+ reportstruct->transit_ready = 1;
+ }
+ }
+ } else if (n == 0) {
+ peerclose = true;
+#ifdef HAVE_THREAD_DEBUG
+ thread_debug("Server thread detected EOF on socket %d", mSettings->mSock);
+#endif
+ } else if ((n < 0) && (FATALTCPREADERR(errno))) {
+ WARN_errno(1, "recv");
+ peerclose = true;
+ n = 0;
+ }
+ currLen += n;
+ }
+ now.setnow();
+ reportstruct->packetTime.tv_sec = now.getSecs();
+ reportstruct->packetTime.tv_usec = now.getUsecs();
+ totLen += currLen;
+ if (isBWSet(mSettings))
+ tokens -= currLen;
+
+ reportstruct->packetLen = currLen;
+ ReportPacket(myReport, reportstruct);
+ // Check for reverse and amount where
+ // the server stops after receiving
+ // the expected byte count
+ if (isReverse(mSettings) && !isModeTime(mSettings) && (totLen >= static_cast<intmax_t>(mSettings->mAmount))) {
+ break;
+ }
+ } else {
+ // Use a 4 usec delay to fill tokens
+ delay_loop(4);
+ }
+ }
+ Done:
+ disarm_itimer();
+ // stop timing
+ now.setnow();
+ reportstruct->packetTime.tv_sec = now.getSecs();
+ reportstruct->packetTime.tv_usec = now.getUsecs();
+ reportstruct->packetLen = 0;
+ if (EndJob(myJob, reportstruct)) {
+#if HAVE_THREAD_DEBUG
+ thread_debug("tcp close sock=%d", mySocket);
+#endif
+ int rc = close(mySocket);
+ WARN_errno(rc == SOCKET_ERROR, "server close");
+ }
+ Iperf_remove_host(mSettings);
+ FreeReport(myJob);
+}
+
+void Server::PostNullEvent () {
+ assert(myReport!=NULL);
+ // push a nonevent into the packet ring
+ // this will cause the reporter to process
+ // up to this event
+ memset(reportstruct, 0, sizeof(struct ReportStruct));
+ now.setnow();
+ reportstruct->packetTime.tv_sec = now.getSecs();
+ reportstruct->packetTime.tv_usec = now.getUsecs();
+ reportstruct->emptyreport=1;
+ ReportPacket(myReport, reportstruct);
+}
+
+inline bool Server::ReadBBWithRXTimestamp () {
+ bool rc = false;
+ int n;
+ while (1) {
+ if ((n = recvn(mySocket, mSettings->mBuf, mSettings->mBounceBackBytes, 0)) == mSettings->mBounceBackBytes) {
+ struct bounceback_hdr *bbhdr = reinterpret_cast<struct bounceback_hdr *>(mSettings->mBuf);
+ uint16_t bbflags = ntohs(bbhdr->bbflags);
+ if (!(bbflags & HEADER_BBSTOP)) {
+ now.setnow();
+ reportstruct->packetTime.tv_sec = now.getSecs();
+ reportstruct->packetTime.tv_usec = now.getUsecs();
+ reportstruct->emptyreport=0;
+ reportstruct->packetLen = mSettings->mBounceBackBytes;
+ // write the rx timestamp back into the payload
+ bbhdr->bbserverRx_ts.sec = htonl(reportstruct->packetTime.tv_sec);
+ bbhdr->bbserverRx_ts.usec = htonl(reportstruct->packetTime.tv_usec);
+ ReportPacket(myReport, reportstruct);
+ rc = true;
+ } else {
+ peerclose = true;
+ }
+ break;
+ } else if (n==0) {
+ peerclose = true;
+ break;
+ } else if (n == -2){
+ PostNullEvent();
+ } else {
+ break;
+ }
+ }
+ return rc;
+}
+
+void Server::RunBounceBackTCP () {
+ if (!InitTrafficLoop())
+ return;
+#if HAVE_DECL_TCP_NODELAY
+ {
+ int nodelay = 1;
+ // set TCP nodelay option
+ int rc = setsockopt(mySocket, IPPROTO_TCP, TCP_NODELAY,
+ reinterpret_cast<char*>(&nodelay), sizeof(nodelay));
+ WARN_errno(rc == SOCKET_ERROR, "setsockopt BB TCP_NODELAY");
+ setNoDelay(mSettings);
+ }
+#endif
+ if (mSettings->mInterval && (mSettings->mIntervalMode == kInterval_Time)) {
+ int sotimer = static_cast<int>(round(mSettings->mInterval / 2.0));
+ SetSocketOptionsSendTimeout(mSettings, sotimer);
+ SetSocketOptionsReceiveTimeout(mSettings, sotimer);
+ } else if (isModeTime(mSettings)) {
+ int sotimer = static_cast<int>(round(mSettings->mAmount * 10000) / 2);
+ SetSocketOptionsSendTimeout(mSettings, sotimer);
+ SetSocketOptionsReceiveTimeout(mSettings, sotimer);
+ }
+ myReport->info.ts.prevsendTime = myReport->info.ts.startTime;
+ now.setnow();
+ reportstruct->packetTime.tv_sec = now.getSecs();
+ reportstruct->packetTime.tv_usec = now.getUsecs();
+ reportstruct->packetLen = mSettings->mBounceBackBytes;
+ reportstruct->emptyreport=0;
+ ReportPacket(myReport, reportstruct);
+
+ while (InProgress()) {
+ int n;
+ reportstruct->emptyreport=1;
+ do {
+ struct bounceback_hdr *bbhdr = reinterpret_cast<struct bounceback_hdr *>(mSettings->mBuf);
+ if (mSettings->mBounceBackHold) {
+#if HAVE_DECL_TCP_QUICKACK
+ if (isTcpQuickAck(mSettings)) {
+ int opt = 1;
+ Socklen_t len = sizeof(opt);
+ int rc = setsockopt(mySocket, IPPROTO_TCP, TCP_QUICKACK,
+ reinterpret_cast<char*>(&opt), len);
+ WARN_errno(rc == SOCKET_ERROR, "setsockopt TCP_QUICKACK");
+ }
+#endif
+ delay_loop(mSettings->mBounceBackHold);
+ }
+ now.setnow();
+ bbhdr->bbserverTx_ts.sec = htonl(now.getSecs());
+ bbhdr->bbserverTx_ts.usec = htonl(now.getUsecs());
+ if (mSettings->mTOS) {
+ bbhdr->tos = htons((uint16_t)(mSettings->mTOS & 0xFF));
+ }
+ if ((n = writen(mySocket, mSettings->mBuf, mSettings->mBounceBackBytes, &reportstruct->writecnt)) == mSettings->mBounceBackBytes) {
+ reportstruct->emptyreport=0;
+ reportstruct->packetLen = n;
+ ReportPacket(myReport, reportstruct);
+ } else {
+ break;
+ }
+ } while (ReadBBWithRXTimestamp());
+ }
+ disarm_itimer();
+ // stop timing
+ now.setnow();
+ reportstruct->packetTime.tv_sec = now.getSecs();
+ reportstruct->packetTime.tv_usec = now.getUsecs();
+ reportstruct->packetLen = 0;
+ if (EndJob(myJob, reportstruct)) {
+#if HAVE_THREAD_DEBUG
+ thread_debug("tcp close sock=%d", mySocket);
+#endif
+ int rc = close(mySocket);
+ WARN_errno(rc == SOCKET_ERROR, "server close");
+ }
+ Iperf_remove_host(mSettings);
+ FreeReport(myJob);
+}
+
+void Server::InitKernelTimeStamping () {
+#if HAVE_DECL_SO_TIMESTAMP
+ iov[0].iov_base=mSettings->mBuf;
+ iov[0].iov_len=mSettings->mBufLen;
+
+ message.msg_iov=iov;
+ message.msg_iovlen=1;
+ message.msg_name=&srcaddr;
+ message.msg_namelen=sizeof(srcaddr);
+
+ message.msg_control = (char *) ctrl;
+ message.msg_controllen = sizeof(ctrl);
+
+ int timestampOn = 1;
+ if (setsockopt(mSettings->mSock, SOL_SOCKET, SO_TIMESTAMP, &timestampOn, sizeof(timestampOn)) < 0) {
+ WARN_errno(mSettings->mSock == SO_TIMESTAMP, "socket");
+ }
+#endif
+}
+
+//
+// Set the report start times and next report times, options
+// are now, the accept time or the first write time
+//
+inline void Server::SetFullDuplexReportStartTime () {
+ assert(myReport->FullDuplexReport != NULL);
+ struct TransferInfo *fullduplexstats = &myReport->FullDuplexReport->info;
+ assert(fullduplexstats != NULL);
+ if (TimeZero(fullduplexstats->ts.startTime)) {
+ fullduplexstats->ts.startTime = myReport->info.ts.startTime;
+ if (isModeTime(mSettings)) {
+ fullduplexstats->ts.nextTime = myReport->info.ts.nextTime;
+ }
+ }
+#ifdef HAVE_THREAD_DEBUG
+ thread_debug("Server fullduplex report start=%ld.%ld next=%ld.%ld", fullduplexstats->ts.startTime.tv_sec, fullduplexstats->ts.startTime.tv_usec, fullduplexstats->ts.nextTime.tv_sec, fullduplexstats->ts.nextTime.tv_usec);
+#endif
+}
+
+inline void Server::SetReportStartTime () {
+ if (TimeZero(myReport->info.ts.startTime)) {
+ if (!TimeZero(mSettings->sent_time) && !isTxStartTime(mSettings)) {
+ // Servers that aren't full duplex use the accept timestamp for start
+ myReport->info.ts.startTime.tv_sec = mSettings->sent_time.tv_sec;
+ myReport->info.ts.startTime.tv_usec = mSettings->sent_time.tv_usec;
+ } else if (!TimeZero(mSettings->accept_time) && !isTxStartTime(mSettings)) {
+ // Servers that aren't full duplex use the accept timestamp for start
+ myReport->info.ts.startTime.tv_sec = mSettings->accept_time.tv_sec;
+ myReport->info.ts.startTime.tv_usec = mSettings->accept_time.tv_usec;
+ } else {
+ now.setnow();
+ myReport->info.ts.startTime.tv_sec = now.getSecs();
+ myReport->info.ts.startTime.tv_usec = now.getUsecs();
+ }
+ }
+ myReport->info.ts.IPGstart = myReport->info.ts.startTime;
+
+ if (!TimeZero(myReport->info.ts.intervalTime)) {
+ myReport->info.ts.nextTime = myReport->info.ts.startTime;
+ TimeAdd(myReport->info.ts.nextTime, myReport->info.ts.intervalTime);
+ }
+ if (myReport->GroupSumReport) {
+ struct TransferInfo *sumstats = &myReport->GroupSumReport->info;
+ assert(sumstats != NULL);
+ Mutex_Lock(&myReport->GroupSumReport->reference.lock);
+ if (TimeZero(sumstats->ts.startTime)) {
+ sumstats->ts.startTime = myReport->info.ts.startTime;
+ if (isModeTime(mSettings) || isModeInfinite(mSettings)) {
+ sumstats->ts.nextTime = myReport->info.ts.nextTime;
+ }
+ }
+ Mutex_Unlock(&myReport->GroupSumReport->reference.lock);
+ }
+#ifdef HAVE_THREAD_DEBUG
+ thread_debug("Server(%d) report start=%ld.%ld next=%ld.%ld", mSettings->mSock, myReport->info.ts.startTime.tv_sec, myReport->info.ts.startTime.tv_usec, myReport->info.ts.nextTime.tv_sec, myReport->info.ts.nextTime.tv_usec);
+#endif
+}
+
+void Server::ClientReverseFirstRead (void) {
+ // Handle the case when the client spawns a server (no listener) and need the initial header
+ // Case of --trip-times and --reverse or --fullduplex, listener handles normal case
+ // Handle the case when the client spawns a server (no listener) and need the initial header
+ // Case of --trip-times and --reverse or --fullduplex, listener handles normal case
+ if (isReverse(mSettings) && (isTripTime(mSettings) || isPeriodicBurst(mSettings) || isIsochronous(mSettings))) {
+ int nread = 0;
+ uint32_t flags = 0;
+ int readlen = 0;
+ if (isUDP(mSettings)) {
+ nread = recvn(mSettings->mSock, mSettings->mBuf, mSettings->mBufLen, 0);
+ switch (nread) {
+ case 0:
+ //peer closed the socket, with no writes e.g. a connect-only test
+ peerclose = true;
+ break;
+ case -1 :
+ FAIL_errno(1, "recvn-reverse", mSettings);
+ break;
+ default :
+ struct client_udp_testhdr *udp_pkt = reinterpret_cast<struct client_udp_testhdr *>(mSettings->mBuf);
+ flags = ntohl(udp_pkt->base.flags);
+ mSettings->sent_time.tv_sec = 0;
+ if (isTripTime(mSettings)) {
+ mSettings->sent_time.tv_sec = ntohl(udp_pkt->start_fq.start_tv_sec);
+ mSettings->sent_time.tv_usec = ntohl(udp_pkt->start_fq.start_tv_usec);
+ }
+ if (!mSettings->sent_time.tv_sec) {
+ now.setnow();
+ mSettings->sent_time.tv_sec = now.getSecs();
+ mSettings->sent_time.tv_usec = now.getUsecs();
+ }
+ reportstruct->packetLen = nread;
+ reportstruct->packetID = 1;
+ break;
+ }
+ } else {
+ nread = recvn(mSettings->mSock, mSettings->mBuf, sizeof(uint32_t), 0);
+ if (nread == 0) {
+ fprintf(stderr, "WARN: zero read on header flags\n");
+ //peer closed the socket, with no writes e.g. a connect-only test
+ peerclose = true;
+ }
+ FAIL_errno((nread < (int) sizeof(uint32_t)), "client read tcp flags", mSettings);
+ reportstruct->packetID = 1;
+ struct client_tcp_testhdr *tcp_pkt = reinterpret_cast<struct client_tcp_testhdr *>(mSettings->mBuf);
+ flags = ntohl(tcp_pkt->base.flags);
+ // figure out the length of the test header
+ if ((readlen = Settings_ClientTestHdrLen(flags, mSettings)) > 0) {
+ // read the test settings passed to the mSettings by the client
+ int adj = (readlen - sizeof(uint32_t));
+ nread = recvn(mSettings->mSock, (mSettings->mBuf + sizeof(uint32_t)), adj, 0);
+ if (nread == 0) {
+ peerclose = true;
+ }
+ FAIL_errno((nread < adj), "client read tcp test info", mSettings);
+ if (nread > 0) {
+ if (isTripTime(mSettings)) {
+ struct client_tcp_testhdr *tcp_pkt = reinterpret_cast<struct client_tcp_testhdr *>(mSettings->mBuf);
+ mSettings->sent_time.tv_sec = ntohl(tcp_pkt->start_fq.start_tv_sec);
+ mSettings->sent_time.tv_usec = ntohl(tcp_pkt->start_fq.start_tv_usec);
+ } else {
+ now.setnow();
+ mSettings->sent_time.tv_sec = now.getSecs();
+ mSettings->sent_time.tv_sec = now.getUsecs();
+ }
+ }
+ mSettings->firstreadbytes = readlen;
+ }
+ }
+ }
+}
+
+bool Server::InitTrafficLoop (void) {
+ bool UDPReady = true;
+ myJob = InitIndividualReport(mSettings);
+ myReport = static_cast<struct ReporterData *>(myJob->this_report);
+ assert(myJob != NULL);
+ if (mSettings->mReportMode == kReport_CSV) {
+ format_ips_port_string(&myReport->info, 0);
+ }
+ // copy the thread drop socket to this object such
+ // that the destructor can close it if needed
+#if defined(HAVE_LINUX_FILTER_H) && defined(HAVE_AF_PACKET)
+ if (mSettings->mSockDrop > 0)
+ myDropSocket = mSettings->mSockDrop;
+#endif
+ // Initialze the reportstruct scratchpad
+ reportstruct = &scratchpad;
+ reportstruct->packetID = 0;
+ reportstruct->l2len = 0;
+ reportstruct->l2errors = 0x0;
+
+ int setfullduplexflag = 0;
+ if (isFullDuplex(mSettings) && !isServerReverse(mSettings)) {
+ assert(mSettings->mFullDuplexReport != NULL);
+ if ((setfullduplexflag = fullduplex_start_barrier(&mSettings->mFullDuplexReport->fullduplex_barrier)) < 0)
+ exit(-1);
+ }
+ Timestamp now;
+ if (isReverse(mSettings)) {
+ mSettings->accept_time.tv_sec = now.getSecs();
+ mSettings->accept_time.tv_usec = now.getUsecs();
+ ClientReverseFirstRead();
+ }
+ if (isTripTime(mSettings)) {
+ if ((abs(now.getSecs() - mSettings->sent_time.tv_sec)) > MAXDIFFTIMESTAMPSECS) {
+ unsetTripTime(mSettings);
+ fprintf(stdout,"WARN: ignore --trip-times because client didn't provide valid start timestamp within %d seconds of now\n", MAXDIFFTIMESTAMPSECS);
+ mSettings->accept_time.tv_sec = now.getSecs();
+ mSettings->accept_time.tv_usec = now.getUsecs();
+ }
+ }
+ SetReportStartTime();
+ reportstruct->prevPacketTime = myReport->info.ts.startTime;
+
+ if (setfullduplexflag)
+ SetFullDuplexReportStartTime();
+
+ if (isServerModeTime(mSettings) || (isModeTime(mSettings) && (isBounceBack(mSettings) || isServerReverse(mSettings) || isFullDuplex(mSettings) || isReverse(mSettings)))) {
+
+ if (isServerReverse(mSettings) || isFullDuplex(mSettings) || isReverse(mSettings))
+ mSettings->mAmount += (SLOPSECS * 100); // add 2 sec for slop on reverse, units are 10 ms
+
+ uintmax_t end_usecs (mSettings->mAmount * 10000); //amount units is 10 ms
+ if (int err = set_itimer(end_usecs))
+ FAIL_errno(err != 0, "setitimer", mSettings);
+ mEndTime.setnow();
+ mEndTime.add(mSettings->mAmount / 100.0);
+ }
+ if (!isSingleUDP(mSettings))
+ PostReport(myJob);
+ // The first payload is different for TCP so read it and report it
+ // before entering the main loop
+ if (mSettings->firstreadbytes > 0) {
+ reportstruct->frameID = 0;
+ reportstruct->packetLen = mSettings->firstreadbytes;
+ if (isUDP(mSettings)) {
+ int offset = 0;
+ UDPReady = !ReadPacketID(offset);
+ reportstruct->packetTime = mSettings->accept_time;
+ } else {
+ reportstruct->sentTime.tv_sec = myReport->info.ts.startTime.tv_sec;
+ reportstruct->sentTime.tv_usec = myReport->info.ts.startTime.tv_usec;
+ reportstruct->packetTime = reportstruct->sentTime;
+ }
+ ReportPacket(myReport, reportstruct);
+ }
+ return UDPReady;
+}
+
+inline int Server::ReadWithRxTimestamp () {
+ long currLen;
+ int tsdone = 0;
+
+#if HAVE_DECL_SO_TIMESTAMP
+ cmsg = reinterpret_cast<struct cmsghdr *>(&ctrl);
+ currLen = recvmsg(mSettings->mSock, &message, mSettings->recvflags);
+ if (currLen > 0) {
+ for (cmsg = CMSG_FIRSTHDR(&message); cmsg != NULL;
+ cmsg = CMSG_NXTHDR(&message, cmsg)) {
+ if (cmsg->cmsg_level == SOL_SOCKET &&
+ cmsg->cmsg_type == SCM_TIMESTAMP &&
+ cmsg->cmsg_len == CMSG_LEN(sizeof(struct timeval))) {
+ memcpy(&(reportstruct->packetTime), CMSG_DATA(cmsg), sizeof(struct timeval));
+ tsdone = 1;
+ }
+ }
+ }
+#else
+ currLen = recv(mSettings->mSock, mSettings->mBuf, mSettings->mBufLen, mSettings->recvflags);
+#endif
+ if (currLen <=0) {
+ // Socket read timeout or read error
+ reportstruct->emptyreport=1;
+ if (currLen == 0) {
+ peerclose = true;
+ } else if (FATALUDPREADERR(errno)) {
+ WARN_errno(1, "recvmsg");
+ currLen = 0;
+ peerclose = true;
+ }
+ } else if (TimeZero(myReport->info.ts.prevpacketTime)) {
+ myReport->info.ts.prevpacketTime = reportstruct->packetTime;
+ }
+ if (!tsdone) {
+ now.setnow();
+ reportstruct->packetTime.tv_sec = now.getSecs();
+ reportstruct->packetTime.tv_usec = now.getUsecs();
+ }
+ return currLen;
+}
+
+// Returns true if the client has indicated this is the final packet
+inline bool Server::ReadPacketID (int offset_adjust) {
+ bool terminate = false;
+ struct UDP_datagram* mBuf_UDP = reinterpret_cast<struct UDP_datagram*>(mSettings->mBuf + offset_adjust);
+ // terminate when datagram begins with negative index
+ // the datagram ID should be correct, just negated
+
+ if (isSeqNo64b(mSettings)) {
+ // New client - Signed PacketID packed into unsigned id2,id
+ reportstruct->packetID = (static_cast<uint32_t>(ntohl(mBuf_UDP->id))) | (static_cast<uintmax_t>(ntohl(mBuf_UDP->id2)) << 32);
+
+#ifdef HAVE_PACKET_DEBUG
+ printf("id 0x%x, 0x%x -> %" PRIdMAX " (0x%" PRIxMAX ")\n",
+ ntohl(mBuf_UDP->id), ntohl(mBuf_UDP->id2), reportstruct->packetID, reportstruct->packetID);
+#endif
+ } else {
+ // Old client - Signed PacketID in Signed id
+ reportstruct->packetID = static_cast<int32_t>(ntohl(mBuf_UDP->id));
+#ifdef HAVE_PACKET_DEBUG
+ printf("id 0x%x -> %" PRIdMAX " (0x%" PRIxMAX ")\n",
+ ntohl(mBuf_UDP->id), reportstruct->packetID, reportstruct->packetID);
+#endif
+ }
+ if (reportstruct->packetID < 0) {
+ reportstruct->packetID = - reportstruct->packetID;
+ terminate = true;
+ }
+ // read the sent timestamp from the rx packet
+ reportstruct->sentTime.tv_sec = ntohl(mBuf_UDP->tv_sec);
+ reportstruct->sentTime.tv_usec = ntohl(mBuf_UDP->tv_usec);
+ return terminate;
+}
+
+void Server::L2_processing () {
+#if (HAVE_LINUX_FILTER_H) && (HAVE_AF_PACKET)
+ eth_hdr = reinterpret_cast<struct ether_header *>(mSettings->mBuf);
+ ip_hdr = reinterpret_cast<struct iphdr *>(mSettings->mBuf + sizeof(struct ether_header));
+ // L4 offest is set by the listener and depends upon IPv4 or IPv6
+ udp_hdr = reinterpret_cast<struct udphdr *>(mSettings->mBuf + mSettings->l4offset);
+ // Read the packet to get the UDP length
+ int udplen = ntohs(udp_hdr->len);
+ //
+ // in the event of an L2 error, double check the packet before passing it to the reporter,
+ // i.e. no reason to run iperf accounting on a packet that has no reasonable L3 or L4 headers
+ //
+ reportstruct->packetLen = udplen - sizeof(struct udphdr);
+ reportstruct->expected_l2len = reportstruct->packetLen + mSettings->l4offset + sizeof(struct udphdr);
+ if (reportstruct->l2len != reportstruct->expected_l2len) {
+ reportstruct->l2errors |= L2LENERR;
+ if (L2_quintuple_filter() != 0) {
+ reportstruct->l2errors |= L2UNKNOWN;
+ reportstruct->l2errors |= L2CSUMERR;
+ reportstruct->emptyreport = 1;
+ }
+ }
+ if (!(reportstruct->l2errors & L2UNKNOWN)) {
+ // perform UDP checksum test, returns zero on success
+ int rc;
+ rc = udpchecksum((void *)ip_hdr, (void *)udp_hdr, udplen, (isIPV6(mSettings) ? 1 : 0));
+ if (rc) {
+ reportstruct->l2errors |= L2CSUMERR;
+ if ((!(reportstruct->l2errors & L2LENERR)) && (L2_quintuple_filter() != 0)) {
+ reportstruct->emptyreport = 1;
+ reportstruct->l2errors |= L2UNKNOWN;
+ }
+ }
+ }
+#endif // HAVE_AF_PACKET
+}
+
+// Run the L2 packet through a quintuple check, i.e. proto/ip src/ip dst/src port/src dst
+// and return zero is there is a match, otherwize return nonzero
+int Server::L2_quintuple_filter () {
+#if defined(HAVE_LINUX_FILTER_H) && defined(HAVE_AF_PACKET)
+
+#define IPV4SRCOFFSET 12 // the ipv4 source address offset from the l3 pdu
+#define IPV6SRCOFFSET 8 // the ipv6 source address offset
+
+ // Get the expected values from the sockaddr structures
+ // Note: it's expected the initiating socket has aready "connected"
+ // and the sockaddr structs have been populated
+ // 2nd Note: sockaddr structs are in network byte order
+ struct sockaddr *p = reinterpret_cast<sockaddr *>(&mSettings->peer);
+ struct sockaddr *l = reinterpret_cast<sockaddr *>(&mSettings->local);
+ // make sure sa_family is coherent for both src and dst
+ if (!(((l->sa_family == AF_INET) && (p->sa_family == AF_INET)) || ((l->sa_family == AF_INET6) && (p->sa_family == AF_INET6)))) {
+ return -1;
+ }
+
+ // check the L2 ethertype
+ struct ether_header *l2hdr = reinterpret_cast<struct ether_header *>(mSettings->mBuf);
+
+ if (!isIPV6(mSettings)) {
+ if (ntohs(l2hdr->ether_type) != ETHERTYPE_IP)
+ return -1;
+ } else {
+ if (ntohs(l2hdr->ether_type) != ETHERTYPE_IPV6)
+ return -1;
+ }
+ // check the ip src/dst
+ const uint32_t *data;
+ udp_hdr = reinterpret_cast<struct udphdr *>(mSettings->mBuf + mSettings->l4offset);
+
+ // Check plain old v4 using v4 addr structs
+ if (l->sa_family == AF_INET) {
+ data = reinterpret_cast<const uint32_t *>(mSettings->mBuf + sizeof(struct ether_header) + IPV4SRCOFFSET);
+ if ((reinterpret_cast<struct sockaddr_in *>(p))->sin_addr.s_addr != *data++)
+ return -1;
+ if ((reinterpret_cast<struct sockaddr_in *>(l))->sin_addr.s_addr != *data)
+ return -1;
+ if (udp_hdr->source != (reinterpret_cast<struct sockaddr_in *>(p))->sin_port)
+ return -1;
+ if (udp_hdr->dest != (reinterpret_cast<struct sockaddr_in *>(l))->sin_port)
+ return -1;
+ } else {
+ // Using the v6 addr structures
+# if HAVE_IPV6
+ struct in6_addr *v6peer = SockAddr_get_in6_addr(&mSettings->peer);
+ struct in6_addr *v6local = SockAddr_get_in6_addr(&mSettings->local);
+ if (isIPV6(mSettings)) {
+ int i;
+ data = reinterpret_cast<const uint32_t *>(mSettings->mBuf + sizeof(struct ether_header) + IPV6SRCOFFSET);
+ // check for v6 src/dst address match
+ for (i = 0; i < 4; i++) {
+ if (v6peer->s6_addr32[i] != *data++)
+ return -1;
+ }
+ for (i = 0; i < 4; i++) {
+ if (v6local->s6_addr32[i] != *data++)
+ return -1;
+ }
+ } else { // v4 addr in v6 family struct
+ data = reinterpret_cast<const uint32_t *>(mSettings->mBuf + sizeof(struct ether_header) + IPV4SRCOFFSET);
+ if (v6peer->s6_addr32[3] != *data++)
+ return -1;
+ if (v6peer->s6_addr32[3] != *data)
+ return -1;
+ }
+ // check udp ports
+ if (udp_hdr->source != (reinterpret_cast<struct sockaddr_in6 *>(p))->sin6_port)
+ return -1;
+ if (udp_hdr->dest != (reinterpret_cast<struct sockaddr_in6 *>(l))->sin6_port)
+ return -1;
+# endif // HAVE_IPV6
+ }
+#endif // HAVE_AF_PACKET
+ // made it through all the checks
+ return 0;
+}
+
+inline void Server::udp_isoch_processing (int rxlen) {
+ reportstruct->transit_ready = 0;
+ // Ignore runt sized isoch packets
+ if (rxlen < static_cast<int>(sizeof(struct UDP_datagram) + sizeof(struct client_hdr_v1) + sizeof(struct client_hdrext) + sizeof(struct isoch_payload))) {
+ reportstruct->burstsize = 0;
+ reportstruct->remaining = 0;
+ reportstruct->frameID = 0;
+ } else {
+ struct client_udp_testhdr *udp_pkt = reinterpret_cast<struct client_udp_testhdr *>(mSettings->mBuf);
+ reportstruct->isochStartTime.tv_sec = ntohl(udp_pkt->isoch.start_tv_sec);
+ reportstruct->isochStartTime.tv_usec = ntohl(udp_pkt->isoch.start_tv_usec);
+ reportstruct->frameID = ntohl(udp_pkt->isoch.frameid);
+ reportstruct->prevframeID = ntohl(udp_pkt->isoch.prevframeid);
+ reportstruct->burstsize = ntohl(udp_pkt->isoch.burstsize);
+ assert(reportstruct->burstsize > 0);
+ reportstruct->burstperiod = ntohl(udp_pkt->isoch.burstperiod);
+ reportstruct->remaining = ntohl(udp_pkt->isoch.remaining);
+ if ((reportstruct->remaining == (uint32_t) rxlen) && ((reportstruct->frameID - reportstruct->prevframeID) == 1)) {
+ reportstruct->transit_ready = 1;
+ }
+ }
+}
+
+/* -------------------------------------------------------------------
+ * Receive UDP data from the (connected) socket.
+ * Sends termination flag several times at the end.
+ * Does not close the socket.
+ * ------------------------------------------------------------------- */
+void Server::RunUDP () {
+ int rxlen;
+ bool isLastPacket = false;
+
+ if (InitTrafficLoop()) {
+ // Exit loop on three conditions
+ // 1) Fatal read error
+ // 2) Last packet of traffic flow sent by client
+ // 3) -t timer expires
+ while (InProgress() && !isLastPacket) {
+ // The emptyreport flag can be set
+ // by any of the packet processing routines
+ // If it's set the iperf reporter won't do
+ // bandwidth accounting, basically it's indicating
+ // that the reportstruct itself couldn't be
+ // completely filled out.
+ reportstruct->emptyreport=1;
+ reportstruct->packetLen=0;
+ // read the next packet with timestamp
+ // will also set empty report or not
+ rxlen=ReadWithRxTimestamp();
+ if (!peerclose && (rxlen > 0)) {
+ reportstruct->emptyreport = 0;
+ reportstruct->packetLen = rxlen;
+ if (isL2LengthCheck(mSettings)) {
+ reportstruct->l2len = rxlen;
+ // L2 processing will set the reportstruct packet length with the length found in the udp header
+ // and also set the expected length in the report struct. The reporter thread
+ // will do the compare and account and print l2 errors
+ reportstruct->l2errors = 0x0;
+ L2_processing();
+ }
+ if (!(reportstruct->l2errors & L2UNKNOWN)) {
+ // ReadPacketID returns true if this is the last UDP packet sent by the client
+ // also sets the packet rx time in the reportstruct
+ reportstruct->prevSentTime = myReport->info.ts.prevsendTime;
+ reportstruct->prevPacketTime = myReport->info.ts.prevpacketTime;
+ isLastPacket = ReadPacketID(mSettings->l4payloadoffset);
+ myReport->info.ts.prevsendTime = reportstruct->sentTime;
+ myReport->info.ts.prevpacketTime = reportstruct->packetTime;
+ if (isIsochronous(mSettings)) {
+ udp_isoch_processing(rxlen);
+ }
+ }
+ }
+ ReportPacket(myReport, reportstruct);
+ }
+ }
+ disarm_itimer();
+ int do_close = EndJob(myJob, reportstruct);
+ if (!isMulticast(mSettings) && !isNoUDPfin(mSettings)) {
+ // send a UDP acknowledgement back except when:
+ // 1) we're NOT receiving multicast
+ // 2) the user requested no final exchange
+ // 3) this is a full duplex test
+ write_UDP_AckFIN(&myReport->info, mSettings->mBufLen);
+ }
+ if (do_close) {
+#if HAVE_THREAD_DEBUG
+ thread_debug("udp close sock=%d", mySocket);
+#endif
+ int rc = close(mySocket);
+ WARN_errno(rc == SOCKET_ERROR, "server close");
+ }
+ Iperf_remove_host(mSettings);
+ FreeReport(myJob);
+}
+
+// end Recv
diff --git a/src/Settings.cpp b/src/Settings.cpp
new file mode 100644
index 0000000..39229f5
--- /dev/null
+++ b/src/Settings.cpp
@@ -0,0 +1,2577 @@
+/*---------------------------------------------------------------
+ * Copyright (c) 1999,2000,2001,2002,2003
+ * The Board of Trustees of the University of Illinois
+ * All Rights Reserved.
+ *---------------------------------------------------------------
+ * 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.
+ * ________________________________________________________________
+ * National Laboratory for Applied Network Research
+ * National Center for Supercomputing Applications
+ * University of Illinois at Urbana-Champaign
+ * http://www.ncsa.uiuc.edu
+ * ________________________________________________________________
+ *
+ * Settings.cpp
+ * by Mark Gates <mgates@nlanr.net>
+ * & Ajay Tirumala <tirumala@ncsa.uiuc.edu>
+ * -------------------------------------------------------------------
+ * Stores and parses the initial values for all the global variables.
+ * -------------------------------------------------------------------
+ * headers
+ * uses
+ * <stdlib.h>
+ * <stdio.h>
+ * <string.h>
+ *
+ * <unistd.h>
+ * ------------------------------------------------------------------- */
+
+#define HEADERS()
+
+#include "headers.h"
+#include "Settings.hpp"
+#include "Locale.h"
+#include "SocketAddr.h"
+#include "util.h"
+#include "version.h"
+#include "gnu_getopt.h"
+#include "isochronous.hpp"
+#include "pdfs.h"
+#include "payloads.h"
+#include "PerfSocket.hpp"
+#include "dscp.h"
+#include "iperf_formattime.h"
+#include <math.h>
+
+static int reversetest = 0;
+static int fullduplextest = 0;
+static int histogram = 0;
+static int jitter_histogram = 0;
+static int l2checks = 0;
+static int incrdstip = 0;
+static int incrsrcip = 0;
+static int incrdstport = 0;
+static int incrsrcport = 0;
+static int sumdstip = 0;
+static int txstarttime = 0;
+static int noconnectsync = 0;
+static int txholdback = 0;
+static int fqrate = 0;
+static int triptime = 0;
+static int infinitetime = 0;
+static int connectonly = 0;
+static int connectretry = 0;
+static int burstipg = 0;
+static int burstsize = 0;
+static int burstperiodic = 0;
+static int isochronous = 0;
+static int noudpfin = 0;
+static int numreportstructs = 0;
+static int sumonly = 0;
+static int so_dontroute = 0;
+static int nearcongest = 0;
+static int permitkey = 0;
+static int permitkeytimeout = 0;
+static int rxwinclamp = 0;
+static int txnotsentlowwater = 0;
+static int tapif = 0;
+static int tunif = 0;
+static int hideips = 0;
+static int bounceback = 0;
+static int bouncebackhold = 0;
+static int bouncebackperiod = 0;
+static int overridetos = 0;
+static int notcpbbquickack = 0;
+static int tcpquickack = 0;
+static int notcpbbquickack_cliset = 0;
+static int workingload = 0;
+static int utctimes = 0;
+static int bouncebackdelaystart = 0;
+static int tcpwritetimes = 0;
+
+void Settings_Interpret(char option, const char *optarg, struct thread_Settings *mExtSettings);
+// apply compound settings after the command line has been fully parsed
+void Settings_ModalOptions(struct thread_Settings *mExtSettings);
+
+static void generate_permit_key(struct thread_Settings *mExtSettings);
+
+/* -------------------------------------------------------------------
+ * command line options
+ *
+ * The option struct essentially maps a long option name (--foobar)
+ * or environment variable ($FOOBAR) to its short option char (f).
+ * ------------------------------------------------------------------- */
+#define LONG_OPTIONS()
+
+const struct option long_options[] =
+{
+{"singleclient", no_argument, NULL, '1'},
+#if 0
+{"v4", no_argument, NULL, '4'},
+{"v6", no_argument, NULL, '6'},
+#endif
+{"bandwidth", required_argument, NULL, 'b'},
+{"client", required_argument, NULL, 'c'},
+{"dualtest", no_argument, NULL, 'd'},
+{"enhanced", no_argument, NULL, 'e'},
+{"format", required_argument, NULL, 'f'},
+{"help", no_argument, NULL, 'h'},
+{"interval", required_argument, NULL, 'i'},
+{"len", required_argument, NULL, 'l'},
+{"print_mss", no_argument, NULL, 'm'},
+{"num", required_argument, NULL, 'n'},
+{"output", required_argument, NULL, 'o'},
+{"port", required_argument, NULL, 'p'},
+{"tradeoff", no_argument, NULL, 'r'},
+{"server", no_argument, NULL, 's'},
+{"time", required_argument, NULL, 't'},
+{"udp", no_argument, NULL, 'u'},
+{"version", no_argument, NULL, 'v'},
+{"window", required_argument, NULL, 'w'},
+{"reportexclude", required_argument, NULL, 'x'},
+{"reportstyle",required_argument, NULL, 'y'},
+{"realtime", no_argument, NULL, 'z'},
+
+// more esoteric options
+{"awdl", no_argument, NULL, 'A'},
+{"bind", required_argument, NULL, 'B'},
+{"bounceback", optional_argument, &bounceback, 1},
+{"bounceback-txdelay", required_argument, &bouncebackdelaystart, 1},
+{"bounceback-hold", required_argument, &bouncebackhold, 1},
+{"bounceback-no-quickack", no_argument, &notcpbbquickack, 1},
+{"bounceback-period", required_argument, &bouncebackperiod, 1},
+{"compatibility", no_argument, NULL, 'C'},
+{"daemon", no_argument, NULL, 'D'},
+{"file_input", required_argument, NULL, 'F'},
+{"ssm-host", required_argument, NULL, 'H'},
+{"stdin_input", no_argument, NULL, 'I'},
+{"mss", required_argument, NULL, 'M'},
+{"nodelay", no_argument, NULL, 'N'},
+{"listenport", required_argument, NULL, 'L'},
+{"parallel", required_argument, NULL, 'P'},
+#ifdef WIN32
+{"remove", no_argument, NULL, 'R'},
+#else
+{"reverse", no_argument, NULL, 'R'},
+#endif
+{"tos", required_argument, NULL, 'S'},
+{"ttl", required_argument, NULL, 'T'},
+{"single_udp", no_argument, NULL, 'U'},
+{"ipv6_domain", no_argument, NULL, 'V'},
+{"suggest_win_size", no_argument, NULL, 'W'},
+{"peer-detect", no_argument, NULL, 'X'},
+{"tcp-congestion", required_argument, NULL, 'Z'},
+{"histograms", optional_argument, &histogram, 1},
+{"hide-ips", no_argument, &hideips, 1},
+{"jitter-histograms", optional_argument, &jitter_histogram, 1},
+{"udp-histograms", optional_argument, &histogram, 1}, // keep support per 2.0.13 usage
+{"l2checks", no_argument, &l2checks, 1},
+{"incr-dstip", no_argument, &incrdstip, 1},
+{"incr-srcip", no_argument, &incrsrcip, 1},
+{"incr-dstport", no_argument, &incrdstport, 1},
+{"incr-srcport", no_argument, &incrsrcport, 1},
+{"sum-dstip", no_argument, &sumdstip, 1},
+{"txstart-time", required_argument, &txstarttime, 1},
+{"txdelay-time", required_argument, &txholdback, 1},
+{"fq-rate", required_argument, &fqrate, 1},
+{"trip-times", no_argument, &triptime, 1},
+{"no-udp-fin", no_argument, &noudpfin, 1},
+{"connect-only", optional_argument, &connectonly, 1},
+{"connect-retries", required_argument, &connectretry, 1},
+{"no-connect-sync", no_argument, &noconnectsync, 1},
+{"full-duplex", no_argument, &fullduplextest, 1},
+{"ipg", required_argument, &burstipg, 1},
+{"isochronous", optional_argument, &isochronous, 1},
+{"sum-only", no_argument, &sumonly, 1},
+{"local-only", optional_argument, &so_dontroute, 1},
+{"near-congestion", optional_argument, &nearcongest, 1},
+{"permit-key", optional_argument, &permitkey, 1},
+{"permit-key-timeout", required_argument, &permitkeytimeout, 1},
+{"burst-size", required_argument, &burstsize, 1},
+{"burst-period", required_argument, &burstperiodic, 1},
+{"tos-override", required_argument, &overridetos, 1},
+{"tcp-rx-window-clamp", required_argument, &rxwinclamp, 1},
+{"tcp-quickack", no_argument, &tcpquickack, 1},
+{"tcp-write-prefetch", required_argument, &txnotsentlowwater, 1}, // see doc/DESIGN_NOTES
+{"tcp-write-times", no_argument, &tcpwritetimes, 1},
+{"tap-dev", optional_argument, &tapif, 1},
+{"tun-dev", optional_argument, &tunif, 1},
+{"working-load", optional_argument, &workingload, 1},
+{"utc", no_argument, &utctimes, 1},
+{"NUM_REPORT_STRUCTS", required_argument, &numreportstructs, 1},
+#ifdef WIN32
+{"reverse", no_argument, &reversetest, 1},
+#endif
+{0, 0, 0, 0}
+};
+
+#define ENV_OPTIONS()
+
+const struct option env_options[] =
+{
+{"IPERF_IPV6_DOMAIN", no_argument, NULL, 'V'},
+{"IPERF_SINGLECLIENT", no_argument, NULL, '1'},
+{"IPERF_BANDWIDTH", required_argument, NULL, 'b'},
+{"IPERF_CLIENT", required_argument, NULL, 'c'},
+{"IPERF_DUALTEST", no_argument, NULL, 'd'},
+{"IPERF_ENHANCEDREPORTS", no_argument, NULL, 'e'},
+{"IPERF_FORMAT", required_argument, NULL, 'f'},
+// skip help
+{"IPERF_INTERVAL", required_argument, NULL, 'i'},
+{"IPERF_LEN", required_argument, NULL, 'l'},
+{"IPERF_PRINT_MSS", no_argument, NULL, 'm'},
+{"IPERF_NUM", required_argument, NULL, 'n'},
+{"IPERF_PORT", required_argument, NULL, 'p'},
+{"IPERF_TRADEOFF", no_argument, NULL, 'r'},
+{"IPERF_SERVER", no_argument, NULL, 's'},
+{"IPERF_TIME", required_argument, NULL, 't'},
+{"IPERF_UDP", no_argument, NULL, 'u'},
+// skip version
+{"TCP_WINDOW_SIZE", required_argument, NULL, 'w'},
+{"IPERF_REPORTEXCLUDE", required_argument, NULL, 'x'},
+{"IPERF_REPORTSTYLE",required_argument, NULL, 'y'},
+
+// more esoteric options
+{"IPERF_BIND", required_argument, NULL, 'B'},
+{"IPERF_COMPAT", no_argument, NULL, 'C'},
+{"IPERF_DAEMON", no_argument, NULL, 'D'},
+{"IPERF_FILE_INPUT", required_argument, NULL, 'F'},
+{"IPERF_STDIN_INPUT", no_argument, NULL, 'I'},
+{"IPERF_MSS", required_argument, NULL, 'M'},
+{"IPERF_NODELAY", no_argument, NULL, 'N'},
+{"IPERF_LISTENPORT", required_argument, NULL, 'L'},
+{"IPERF_PARALLEL", required_argument, NULL, 'P'},
+{"IPERF_TOS", required_argument, NULL, 'S'},
+{"IPERF_TTL", required_argument, NULL, 'T'},
+{"IPERF_SINGLE_UDP", no_argument, NULL, 'U'},
+{"IPERF_SUGGEST_WIN_SIZE", required_argument, NULL, 'W'},
+{"IPERF_PEER_DETECT", no_argument, NULL, 'X'},
+{"IPERF_CONGESTION_CONTROL", required_argument, NULL, 'Z'},
+{0, 0, 0, 0}
+};
+
+#define SHORT_OPTIONS()
+
+const char short_options[] = "146b:c:def:hi:l:mn:o:p:rst:uvw:x:y:zAB:CDF:H:IL:M:NP:RS:T:UVWXZ:";
+
+/* -------------------------------------------------------------------
+ * defaults
+ * ------------------------------------------------------------------- */
+#define DEFAULTS()
+
+const long kDefault_UDPRate = 1024 * 1024; // -u if set, 1 Mbit/sec
+const int kDefault_TCPBufLen = 128 * 1024; // TCP default read/write size
+const int kDefault_BBTCPBufLen = 100; // default bounce-back size in bytes
+
+
+/* -------------------------------------------------------------------
+ * Initialize all settings to defaults.
+ * ------------------------------------------------------------------- */
+void Settings_Initialize (struct thread_Settings *main) {
+ // Everything defaults to zero or NULL with
+ // this memset. Only need to set non-zero values
+ // below.
+ memset(main, 0, sizeof(struct thread_Settings));
+ main->mSock = INVALID_SOCKET;
+ main->mReportMode = kReport_Default;
+ // option, defaults
+ main->flags = FLAG_MODETIME | FLAG_STDOUT; // Default time and stdout
+ main->flags_extend = 0x0; // Default all extend flags to off
+ main->flags_extend2 = 0x0; // Default all extend flags to off
+ //main->mAppRate = 0; // -b, offered (or rate limited) load (both UDP and TCP)
+ main->mAppRateUnits = kRate_BW;
+ //main->mHost = NULL; // -c, none, required for client
+ main->mMode = kTest_Normal; // -d, mMode == kTest_DualTest
+ main->mFormat = 'a'; // -f, adaptive bits
+ // skip help // -h,
+ //main->mBufLenSet = false; // -l,
+ main->mBufLen = kDefault_TCPBufLen; // -l, Default to TCP read/write size
+ //main->mInterval = 0; // -i, ie. no periodic bw reports
+ //main->mPrintMSS = false; // -m, don't print MSS
+ // mAmount is time also // -n, N/A
+ //main->mOutputFileName = NULL; // -o, filename
+ main->mPort = 5001; // -p, ttcp port
+ main->mBindPort = 0; // -B, default port for bind
+ // mMode = kTest_Normal; // -r, mMode == kTest_TradeOff
+ main->mThreadMode = kMode_Unknown; // -s, or -c, none
+ main->mAmount = 1000; // -t, 10 seconds, units is 10 ms
+ main->mIntervalMode = kInterval_None;// -i none, time, packets, or bursts
+ // skip version // -v,
+ //main->mTCPWin = 0; // -w, ie. don't set window
+
+ // more esoteric options
+ //main->mLocalhost = NULL; // -B, none
+ //main->mCompat = false; // -C, run in Compatibility mode
+ //main->mDaemon = false; // -D, run as a daemon
+ //main->mFileInput = false; // -F,
+ //main->mFileName = NULL; // -F, filename
+ //main->mStdin = false; // -I, default not stdin
+ //main->mListenPort = 0; // -L, listen port
+ //main->mMSS = 0; // -M, ie. don't set MSS
+ //main->mNodelay = false; // -N, don't set nodelay
+ //main->mThreads = 0; // -P,
+ //main->mRemoveService = false; // -R,
+ //main->mTOS = 0; // -S, ie. don't set type of service
+ main->mTTL = -1; // -T, link-local TTL
+ //main->mDomain = kMode_IPv4; // -V,
+ //main->mSuggestWin = false; // -W, Suggest the window size.
+ main->mListenerTimeout = -1; //
+ main->mKeyCheck = true;
+#if defined(HAVE_DECL_SO_DONTROUTE) && defined(HAVE_DEFAULT_DONTROUTE_ON)
+ setDontRoute(main);
+#endif
+ main->mFPS = 1;
+} // end Settings
+
+void Settings_Copy (struct thread_Settings *from, struct thread_Settings **into, int copyall) {
+ *into = new struct thread_Settings;
+ memset(*into, 0, sizeof(struct thread_Settings));
+ memcpy(*into, from, sizeof(struct thread_Settings));
+ (*into)->mSumReport = NULL;
+ (*into)->mTransferIDStr = NULL;
+
+#ifdef HAVE_THREAD_DEBUG
+ thread_debug("Copy thread settings (malloc) from/to=%p/%p report/sum/fullduplex %p/%p/%p", \
+ (void *)from, (void *)*into, (void *)(*into)->reporthdr, (void *)(*into)->mSumReport, (void *)(*into)->mFullDuplexReport);
+#endif
+ // Some settings don't need to be copied and will confuse things. Don't copy them unless copyall is set
+ if (copyall) {
+ // Don't allocate memory for these if this is a reverse client
+ if (from->mHost != NULL) {
+ (*into)->mHost = new char[strlen(from->mHost) + 1];
+ strcpy((*into)->mHost, from->mHost);
+ }
+ if (from->mOutputFileName != NULL) {
+ (*into)->mOutputFileName = new char[strlen(from->mOutputFileName) + 1];
+ strcpy((*into)->mOutputFileName, from->mOutputFileName);
+ }
+ if (from->mLocalhost != NULL) {
+ (*into)->mLocalhost = new char[strlen(from->mLocalhost) + 1];
+ strcpy((*into)->mLocalhost, from->mLocalhost);
+ }
+ if (from->mFileName != NULL) {
+ (*into)->mFileName = new char[strlen(from->mFileName) + 1];
+ strcpy((*into)->mFileName, from->mFileName);
+ }
+ if (from->mHistogramStr != NULL) {
+ (*into)->mHistogramStr = new char[strlen(from->mHistogramStr) + 1];
+ strcpy((*into)->mHistogramStr, from->mHistogramStr);
+ }
+ if (from->mSSMMulticastStr != NULL) {
+ (*into)->mSSMMulticastStr = new char[strlen(from->mSSMMulticastStr) + 1];
+ strcpy((*into)->mSSMMulticastStr, from->mSSMMulticastStr);
+ }
+ if (from->mIfrname != NULL) {
+ (*into)->mIfrname = static_cast<char *>(calloc(strlen(from->mIfrname) + 1, sizeof(char)));
+ strcpy((*into)->mIfrname, from->mIfrname);
+ }
+ if (from->mIfrnametx != NULL) {
+ (*into)->mIfrnametx = static_cast<char *>(calloc(strlen(from->mIfrnametx) + 1, sizeof(char)));
+ strcpy((*into)->mIfrnametx, from->mIfrnametx);
+ }
+ if (from->mIsochronousStr != NULL) {
+ (*into)->mIsochronousStr = new char[strlen(from->mIsochronousStr) + 1];
+ strcpy((*into)->mIsochronousStr, from->mIsochronousStr);
+ }
+ if (from->mHideHost != NULL) {
+ (*into)->mHideHost = new char[strlen(from->mHideHost) + 1];
+ strcpy((*into)->mHideHost, from->mHideHost);
+ }
+ if (from->mCongestion != NULL) {
+ (*into)->mCongestion = new char[strlen(from->mCongestion) + 1];
+ strcpy((*into)->mCongestion, from->mCongestion);
+ }
+ } else {
+ (*into)->mHost = NULL;
+ (*into)->mOutputFileName = NULL;
+ (*into)->mLocalhost = NULL;
+ (*into)->mFileName = NULL;
+ (*into)->mHistogramStr = NULL;
+ (*into)->mSSMMulticastStr = NULL;
+ (*into)->mIfrname = NULL;
+ (*into)->mIfrnametx = NULL;
+ (*into)->mIsochronousStr = NULL;
+ (*into)->mCongestion = NULL;
+ // apply the server side congestion setting to reverse clients
+ if (from->mIsochronousStr != NULL) {
+ (*into)->mIsochronousStr = new char[strlen(from->mIsochronousStr) + 1];
+ strcpy((*into)->mIsochronousStr, from->mIsochronousStr);
+ }
+ }
+
+ (*into)->txstart_epoch = from->txstart_epoch;
+ (*into)->mSumReport = from->mSumReport;
+ (*into)->mFullDuplexReport = from->mFullDuplexReport;
+
+ // Zero out certain entries
+ (*into)->mTID = thread_zeroid();
+ (*into)->runNext = NULL;
+ (*into)->runNow = NULL;
+#if defined(HAVE_LINUX_FILTER_H) && defined(HAVE_AF_PACKET)
+ (*into)->mSockDrop = INVALID_SOCKET;
+#endif
+ int mbuflen = (from->mBufLen > MINMBUFALLOCSIZE) ? from->mBufLen : MINMBUFALLOCSIZE; // defined in payloads.h
+#if (((HAVE_TUNTAP_TUN) || (HAVE_TUNTAP_TAP)) && (AF_PACKET))
+ mbuflen += TAPBYTESSLOP;
+#endif
+ (*into)->mBuf = new char[mbuflen];
+ memset((*into)->mBuf, 0, mbuflen);
+
+#ifdef HAVE_THREAD_DEBUG
+ thread_debug("Copy Settings: MBUF malloc %d bytes (%p)", mbuflen, (void *) (*into)->mBuf);
+#endif
+ Condition_Initialize(&(*into)->awake_me);
+ // default copied settings to no reporter reporting
+ unsetReport((*into));
+}
+
+/* -------------------------------------------------------------------
+ * Delete memory: Does not clean up open file pointers or ptr_parents
+ * ------------------------------------------------------------------- */
+
+void Settings_Destroy (struct thread_Settings *mSettings) {
+#if HAVE_THREAD_DEBUG
+ thread_debug("Free thread settings=%p", mSettings);
+#endif
+ if (mSettings->tuntapdev)
+ close(mSettings->tuntapdev);
+ Condition_Destroy(&mSettings->awake_me);
+ DELETE_ARRAY(mSettings->mHost);
+ DELETE_ARRAY(mSettings->mHideHost);
+ DELETE_ARRAY(mSettings->mLocalhost);
+ DELETE_ARRAY(mSettings->mFileName);
+ DELETE_ARRAY(mSettings->mOutputFileName);
+ DELETE_ARRAY(mSettings->mHistogramStr);
+ DELETE_ARRAY(mSettings->mSSMMulticastStr);
+ DELETE_ARRAY(mSettings->mCongestion);
+ FREE_ARRAY(mSettings->mIfrname);
+ FREE_ARRAY(mSettings->mIfrnametx);
+ FREE_ARRAY(mSettings->mTransferIDStr);
+ DELETE_ARRAY(mSettings->mIsochronousStr);
+ DELETE_ARRAY(mSettings->mBuf);
+ DELETE_PTR(mSettings);
+} // end ~Settings
+
+/* -------------------------------------------------------------------
+ * Parses settings from user's environment variables.
+ * ------------------------------------------------------------------- */
+void Settings_ParseEnvironment (struct thread_Settings *mSettings) {
+ char *theVariable;
+
+ int i = 0;
+ while (env_options[i].name != NULL) {
+ theVariable = getenv(env_options[i].name);
+ if (theVariable != NULL) {
+ Settings_Interpret(env_options[i].val, theVariable, mSettings);
+ }
+ i++;
+ }
+} // end ParseEnvironment
+
+/* -------------------------------------------------------------------
+ * Parse settings from app's command line.
+ * ------------------------------------------------------------------- */
+
+void Settings_ParseCommandLine (int argc, char **argv, struct thread_Settings *mSettings) {
+ int option;
+ gnu_opterr = 1; // Fail on an unrecognized command line option
+ while ((option =
+ gnu_getopt_long(argc, argv, short_options,
+ long_options, NULL)) != EOF) {
+ Settings_Interpret(option, gnu_optarg, mSettings);
+ }
+
+ for (int i = gnu_optind; i < argc; i++) {
+ fprintf(stderr, "%s: ignoring extra argument -- %s\n", argv[0], argv[i]);
+ }
+ // Determine the modal or compound settings now that the full command line has been parsed
+ Settings_ModalOptions(mSettings);
+
+} // end ParseCommandLine
+
+/* -------------------------------------------------------------------
+ * Interpret individual options, either from the command line
+ * or from environment variables.
+ * ------------------------------------------------------------------- */
+
+void Settings_Interpret (char option, const char *optarg, struct thread_Settings *mExtSettings) {
+ char *results;
+ switch (option) {
+ case '1': // Single Client
+ setSingleClient(mExtSettings);
+ break;
+
+ case '4': // v4 only
+ setIPV4(mExtSettings);
+ break;
+
+ case '6': // v6 only
+ setIPV6(mExtSettings);
+ break;
+
+ case 'b': // UDP bandwidth
+ {
+ char *tmp= new char [strlen(optarg) + 1];
+ strcpy(tmp, optarg);
+ // scan for PPS units, just look for 'p' as that's good enough
+ if ((((results = strtok(tmp, "p")) != NULL) && strcmp(results,optarg) != 0) \
+ || (((results = strtok(tmp, "P")) != NULL) && strcmp(results,optarg) != 0)) {
+ mExtSettings->mAppRateUnits = kRate_PPS;
+ mExtSettings->mAppRate = byte_atoi(results);
+ } else {
+ mExtSettings->mAppRateUnits = kRate_BW;
+ mExtSettings->mAppRate = byte_atoi(optarg);
+ if (((results = strtok(tmp, ",")) != NULL) && strcmp(results,optarg) != 0) {
+ setVaryLoad(mExtSettings);
+ mExtSettings->mVariance = byte_atoi(optarg);
+ }
+ }
+ delete [] tmp;
+ }
+ setBWSet(mExtSettings);
+ break;
+ case 'c': // client mode w/ server host to connect to
+ mExtSettings->mHost = new char[strlen(optarg) + 1 ];
+ strcpy(mExtSettings->mHost, optarg);
+
+ if (mExtSettings->mThreadMode == kMode_Unknown) {
+ mExtSettings->mThreadMode = kMode_Client;
+ mExtSettings->mThreads = 1;
+ }
+ break;
+
+ case 'd': // Dual-test Mode
+ if (mExtSettings->mThreadMode != kMode_Client) {
+ fprintf(stderr, warn_invalid_server_option, option);
+ break;
+ }
+ if (isCompat(mExtSettings)) {
+ fprintf(stderr, warn_invalid_compatibility_option, option);
+ }
+#ifdef HAVE_THREAD
+ mExtSettings->mMode = kTest_DualTest;
+#else
+ fprintf(stderr, warn_invalid_single_threaded, option);
+ mExtSettings->mMode = kTest_TradeOff;
+#endif
+ break;
+ case 'e': // Use enhanced reports
+ setEnhanced(mExtSettings);
+ break;
+ case 'f': // format to print in
+ mExtSettings->mFormat = (*optarg);
+ break;
+
+ case 'h': // print help and exit
+ fprintf(stdout, "%s", usage_long1);
+ fprintf(stdout, "%s", usage_long2);
+ exit(0);
+ break;
+
+ case 'i': // specify interval between periodic bw reports
+ {
+ char framechar;
+ char *tmp= new char [strlen(optarg) + 1];
+ strcpy(tmp, optarg);
+ // scan for frames as units
+ if ((sscanf(optarg,"%c", &framechar)) && ((framechar == 'f') || (framechar == 'F'))) {
+ mExtSettings->mIntervalMode = kInterval_Frames;
+ setEnhanced(mExtSettings);
+ setFrameInterval(mExtSettings);
+ } else {
+ char *end;
+ strcpy(tmp, optarg);
+ double itime = strtof(optarg, &end);
+ if (*end != '\0') {
+ fprintf (stderr, "Invalid value of '%s' for -i interval\n", optarg);
+ exit(1);
+ }
+ if (itime > (UINT_MAX / 1e6)) {
+ fprintf (stderr, "Too large value of '%s' for -i interval, max is %f\n", optarg, (UINT_MAX / 1e6));
+ exit(1);
+ }
+ mExtSettings->mInterval = static_cast<unsigned int>(ceil(itime * 1e6));
+ mExtSettings->mIntervalMode = kInterval_Time;
+ if (mExtSettings->mInterval <= 0) {
+ mExtSettings->mInterval = -1;
+ } else if (mExtSettings->mInterval < SMALLEST_INTERVAL) {
+ mExtSettings->mInterval = SMALLEST_INTERVAL;
+ fprintf (stderr, report_interval_small, (double) mExtSettings->mInterval / 1e3);
+ }
+ }
+ delete [] tmp;
+ }
+ break;
+
+ case 'l': // length of each buffer
+ mExtSettings->mBufLen = byte_atoi(optarg);
+ setBuflenSet(mExtSettings);
+ break;
+
+ case 'm': // print TCP MSS
+#if HAVE_DECL_TCP_MAXSEG
+ setPrintMSS(mExtSettings);
+#endif
+ break;
+
+ case 'n': // bytes of data
+ // amount mode (instead of time mode)
+ unsetModeTime(mExtSettings);
+ mExtSettings->mAmount = byte_atoi(optarg);
+ if (!(mExtSettings->mAmount > 0)) {
+ fprintf (stderr, "Invalid value for -n amount of '%s'\n", optarg);
+ exit(1);
+ }
+ break;
+
+ case 'o' : // output the report and other messages into the file
+ unsetSTDOUT(mExtSettings);
+ mExtSettings->mOutputFileName = new char[strlen(optarg)+1];
+ strcpy(mExtSettings->mOutputFileName, optarg);
+ break;
+
+ case 'p': // server port
+ {
+ char *tmp= new char [strlen(optarg) + 1];
+ strcpy(tmp, optarg);
+ if ((results = strtok(tmp, "-")) != NULL) {
+ mExtSettings->mPort = atoi(results);
+ if (strcmp(results,optarg)) {
+ mExtSettings->mPortLast = atoi(strtok(NULL, "-"));
+ setIncrDstPort(mExtSettings);
+ }
+ }
+ delete [] tmp;
+ break;
+ }
+ case 'r': // test mode tradeoff
+ if (mExtSettings->mThreadMode != kMode_Client) {
+ fprintf(stderr, warn_invalid_server_option, option);
+ break;
+ }
+ if (isCompat(mExtSettings)) {
+ fprintf(stderr, warn_invalid_compatibility_option, option);
+ }
+ mExtSettings->mMode = kTest_TradeOff;
+ break;
+
+ case 's': // server mode
+ if (mExtSettings->mThreadMode != kMode_Unknown) {
+ fprintf(stderr, warn_invalid_client_option, option);
+ break;
+ }
+
+ mExtSettings->mThreadMode = kMode_Listener;
+ break;
+
+ case 't': // seconds to run the client, server, listener
+ if (!optarg) {
+ fprintf(stderr, "ERROR: option -t requires value\n");
+ exit(1);
+ }
+ {
+ double val;
+#if HAVE_STRTOD
+ char *end;
+ errno = 0;
+ val = strtod(optarg, &end);
+ if (errno || (*end != '\0')) {
+ fprintf(stderr, "ERROR: -t value of '%s' not recognized\n", optarg);
+ exit(1);
+ }
+#else
+ val = atof(optarg);
+#endif
+ if (val > 0.0)
+ mExtSettings->mAmount = static_cast<size_t>(val * 100.0);
+ else
+ infinitetime = 1;
+ // time mode (instead of amount mode), units is 10 ms
+ setModeTime(mExtSettings);
+ setServerModeTime(mExtSettings);
+ }
+ break;
+ case 'u': // UDP instead of TCP
+ setUDP(mExtSettings);
+ break;
+
+ case 'v': // print version and exit
+ fprintf(stdout, "%s", version);
+ exit(0);
+ break;
+
+ case 'w': // TCP window size (socket buffer size)
+ mExtSettings->mTCPWin = byte_atoi(optarg);
+ if (mExtSettings->mTCPWin < 2048) {
+ fprintf(stderr, warn_window_small, mExtSettings->mTCPWin);
+ }
+ break;
+
+ case 'x': // Limit Reports
+ while (*optarg != '\0') {
+ switch (*optarg) {
+ case 's':
+ case 'S':
+ setNoSettReport(mExtSettings);
+ break;
+ case 'c':
+ case 'C':
+ setNoConnReport(mExtSettings);
+ break;
+ case 'd':
+ case 'D':
+ setNoDataReport(mExtSettings);
+ break;
+ case 'v':
+ case 'V':
+ setNoServReport(mExtSettings);
+ break;
+ case 'm':
+ case 'M':
+ setNoMultReport(mExtSettings);
+ break;
+ default:
+ fprintf(stderr, warn_invalid_report, *optarg);
+ }
+ optarg++;
+ }
+ break;
+#if HAVE_SCHED_SETSCHEDULER
+ case 'z': // Use realtime scheduling
+ setRealtime(mExtSettings);
+ break;
+#endif
+
+ case 'y': // Reporting Style
+ switch (*optarg) {
+ case 'c':
+ case 'C':
+ mExtSettings->mReportMode = kReport_CSV;
+ setNoSettReport(mExtSettings);
+ setNoConnReport(mExtSettings);
+ break;
+ default:
+ fprintf(stderr, warn_invalid_report_style, optarg);
+ }
+ break;
+
+ // more esoteric options
+
+ case 'B': // specify bind address
+ if (mExtSettings->mLocalhost == NULL) {
+ mExtSettings->mLocalhost = new char[strlen(optarg) + 1 ];
+ strcpy(mExtSettings->mLocalhost, optarg);
+ }
+ break;
+
+ case 'C': // Run in Compatibility Mode, i.e. no intial nor final header messaging
+ setCompat(mExtSettings);
+ if (mExtSettings->mMode != kTest_Normal) {
+ fprintf(stderr, warn_invalid_compatibility_option,
+ (mExtSettings->mMode == kTest_DualTest ?
+ 'd' : 'r'));
+ mExtSettings->mMode = kTest_Normal;
+ }
+ break;
+
+ case 'D': // Run as a daemon
+ setDaemon(mExtSettings);
+ break;
+
+ case 'F' : // Get the input for the data stream from a file
+ if (mExtSettings->mThreadMode != kMode_Client) {
+ fprintf(stderr, warn_invalid_server_option, option);
+ break;
+ }
+
+ setFileInput(mExtSettings);
+ mExtSettings->mFileName = new char[strlen(optarg)+1];
+ strcpy(mExtSettings->mFileName, optarg);
+ break;
+
+ case 'H' : // Get the SSM host (or Source per the S,G)
+ if (mExtSettings->mThreadMode == kMode_Client) {
+ fprintf(stderr, warn_invalid_client_option, option);
+ break;
+ }
+ mExtSettings->mSSMMulticastStr = new char[strlen(optarg)+1];
+ strcpy(mExtSettings->mSSMMulticastStr, optarg);
+ setSSMMulticast(mExtSettings);
+ break;
+
+ case 'I' : // Set the stdin as the input source
+ if (mExtSettings->mThreadMode != kMode_Client) {
+ fprintf(stderr, warn_invalid_server_option, option);
+ break;
+ }
+
+ setFileInput(mExtSettings);
+ setSTDIN(mExtSettings);
+ mExtSettings->mFileName = new char[strlen("<stdin>")+1];
+ strcpy(mExtSettings->mFileName,"<stdin>");
+ break;
+
+ case 'L': // Listen Port (bidirectional testing client-side)
+ if (mExtSettings->mThreadMode != kMode_Client) {
+ fprintf(stderr, warn_invalid_server_option, option);
+ break;
+ }
+ mExtSettings->mListenPort = atoi(optarg);
+ break;
+
+ case 'M': // specify TCP MSS (maximum segment size)
+#if HAVE_DECL_TCP_MAXSEG
+ mExtSettings->mMSS = byte_atoi(optarg);
+ setTCPMSS(mExtSettings);
+ setPrintMSS(mExtSettings);
+#endif
+ break;
+
+ case 'N': // specify TCP nodelay option (disable Jacobson's Algorithm)
+ setNoDelay(mExtSettings);
+ break;
+
+ case 'P': // number of client threads
+#ifdef HAVE_THREAD
+ mExtSettings->mThreads = atoi(optarg);
+#else
+ if (mExtSettings->mThreadMode != kMode_Server) {
+ fprintf(stderr, warn_invalid_single_threaded, option);
+ } else {
+ mExtSettings->mThreads = atoi(optarg);
+ }
+#endif
+ break;
+#ifdef WIN32
+ case 'R':
+ setRemoveService(mExtSettings);
+ break;
+#else
+ case 'R':
+ setReverse(mExtSettings);
+ break;
+#endif
+ case 'S': // IP type-of-service
+ // TODO use a function that understands base-2
+ // the zero base here allows the user to specify
+ // "0x#" hex, "0#" octal, and "#" decimal numbers
+ if ((mExtSettings->mTOS = parse_ipqos(optarg)) == -1) {
+ fprintf(stderr, "Invalid --tos value of %s\n", optarg);
+ mExtSettings->mTOS = 0;
+ }
+ break;
+
+ case 'T': // time-to-live for both unicast and multicast
+ mExtSettings->mTTL = atoi(optarg);
+ break;
+
+ case 'U': // single threaded UDP server
+ setUDP(mExtSettings);
+ setSingleUDP(mExtSettings);
+ setSingleClient(mExtSettings);
+ break;
+
+ case 'V': // IPv6 Domain
+#if HAVE_IPV6
+ setIPV6(mExtSettings);
+#else
+ fprintf(stderr, "The --ipv6_domain (-V) option is not enabled in this build.\n");
+ exit(1);
+#endif
+ break;
+
+ case 'W' :
+ setSuggestWin(mExtSettings);
+ fprintf(stderr, "The -W option is not available in this release\n");
+ break;
+
+ case 'X' :
+ setPeerVerDetect(mExtSettings);
+ break;
+
+ case 'Z':
+#ifdef TCP_CONGESTION
+ setCongestionControl(mExtSettings);
+ mExtSettings->mCongestion = new char[strlen(optarg)+1];
+ strcpy(mExtSettings->mCongestion, optarg);
+#else
+ fprintf(stderr, "The -Z option is not available on this operating system\n");
+#endif
+ break;
+
+ case 0:
+ if (incrdstip) {
+ incrdstip = 0;
+ setIncrDstIP(mExtSettings);
+ }
+ if (incrdstport) {
+ incrdstport = 0;
+ setIncrDstPort(mExtSettings);
+ }
+ if (incrsrcip) {
+ incrsrcip = 0;
+ setIncrSrcIP(mExtSettings);
+ }
+ if (incrsrcport) {
+ incrsrcport = 0;
+ setIncrSrcPort(mExtSettings);
+ }
+ if (sumdstip) {
+ sumdstip = 0;
+ setSumServerDstIP(mExtSettings);
+ }
+ if (txstarttime) {
+ long seconds;
+ long usecs;
+ int match = 0;
+ txstarttime = 0;
+ setTxStartTime(mExtSettings);
+ setEnhanced(mExtSettings);
+ match = sscanf(optarg,"%ld.%6ld", &seconds, &usecs);
+ mExtSettings->txstart_epoch.tv_usec = 0;
+ switch (match) {
+ case 2:
+ mExtSettings->txstart_epoch.tv_usec = usecs;
+ case 1:
+ mExtSettings->txstart_epoch.tv_sec = seconds;
+ break;
+ default:
+ unsetTxStartTime(mExtSettings);
+ fprintf(stderr, "WARNING: invalid --txstart-time format\n");
+ }
+ }
+ if (noconnectsync) {
+#ifdef HAVE_THREAD
+ noconnectsync = 0;
+ setNoConnectSync(mExtSettings);
+#else
+ fprintf(stderr, "WARNING: --no-connect-sync requires thread support and not supported\n");
+#endif
+ }
+ if (txholdback) {
+ txholdback = 0;
+ char *end;
+ Timestamp holdbackdelay;
+ double delay = strtof(optarg, &end);
+ if (*end != '\0') {
+ fprintf (stderr, "Invalid value of '%s' for --txdelay-time time\n", optarg);
+ } else {
+ holdbackdelay.set(delay);
+ mExtSettings->txholdback_timer.tv_sec = holdbackdelay.getSecs();
+ mExtSettings->txholdback_timer.tv_usec = (holdbackdelay.getUsecs());
+ setTxHoldback(mExtSettings);
+ }
+ }
+ if (triptime) {
+ triptime = 0;
+ setTripTime(mExtSettings);
+ }
+ if (noudpfin) {
+ noudpfin = 0;
+ setNoUDPfin(mExtSettings);
+ }
+ if (connectonly) {
+ connectonly = 0;
+ setConnectOnly(mExtSettings);
+ unsetNoConnReport(mExtSettings);
+ if (optarg) {
+ mExtSettings->connectonly_count = atoi(optarg);
+ } else {
+ mExtSettings->connectonly_count = -1;
+ }
+ }
+ if (connectretry) {
+ connectretry = 0;
+ mExtSettings->mConnectRetries = atoi(optarg);
+ }
+ if (sumonly) {
+ sumonly = 0;
+ setSumOnly(mExtSettings);
+ }
+ if (so_dontroute) {
+ so_dontroute = 0;
+#if HAVE_DECL_SO_DONTROUTE
+ setDontRoute(mExtSettings);
+ if (optarg) {
+ if (atoi(optarg))
+ setDontRoute(mExtSettings);
+ else
+ unsetDontRoute(mExtSettings);
+ }
+#else
+ fprintf(stderr, "WARNING: The --local-only option is not supported on this platform\n");
+#endif
+ }
+ if (nearcongest) {
+ nearcongest = 0;
+ setNearCongest(mExtSettings);
+ if (optarg && (atof(optarg) >= 0.0)) {
+ mExtSettings->rtt_nearcongest_weight_factor = atof(optarg);
+ } else {
+ mExtSettings->rtt_nearcongest_weight_factor = NEARCONGEST_DEFAULT;
+ }
+ }
+ if (permitkey) {
+ permitkey = 0;
+ if (optarg) {
+ strncpy(mExtSettings->mPermitKey, optarg, MAX_PERMITKEY_LEN);
+ mExtSettings->mPermitKey[MAX_PERMITKEY_LEN] = '\0';
+ } else {
+ mExtSettings->mPermitKey[0] = '\0';
+ }
+ setPermitKey(mExtSettings);
+ }
+ if (permitkeytimeout) {
+ permitkeytimeout = 0;
+ if (atof(optarg) >= 0.0)
+ mExtSettings->mListenerTimeout = static_cast<size_t>(atof(optarg));
+ }
+ if (histogram) {
+ histogram = 0;
+ setHistogram(mExtSettings);
+ setEnhanced(mExtSettings);
+ if (optarg) {
+ mExtSettings->mHistogramStr = new char[strlen(optarg) + 1 ];
+ strcpy(mExtSettings->mHistogramStr, optarg);
+ } else {
+ mExtSettings->mHistogramStr = NULL;
+ }
+ }
+ if (jitter_histogram) {
+ jitter_histogram = 0;
+ setJitterHistogram(mExtSettings);
+ setEnhanced(mExtSettings);
+ if (optarg) {
+ mExtSettings->jitter_binwidth = atoi(optarg);
+ } else {
+ mExtSettings->jitter_binwidth = JITTER_BINWIDTH; // units are usecs
+ }
+ }
+ if (reversetest) {
+ reversetest = 0;
+ setReverse(mExtSettings);
+ }
+ if (fullduplextest) {
+ fullduplextest = 0;
+ setFullDuplex(mExtSettings);
+ }
+ if (overridetos) {
+ overridetos = 0;
+ if ((mExtSettings->mRTOS = parse_ipqos(optarg)) == -1) {
+ fprintf(stderr, "Invalid --tos-overide value of %s\n", optarg);
+ } else {
+ setOverrideTOS(mExtSettings);
+ }
+ }
+ if (fqrate) {
+#if defined(HAVE_DECL_SO_MAX_PACING_RATE)
+ fqrate=0;
+ setFQPacing(mExtSettings);
+ mExtSettings->mFQPacingRate = static_cast<uintmax_t>(bitorbyte_atoi(optarg) / 8);
+#else
+ fprintf(stderr, "WARNING: The --fq-rate option is not supported\n");
+#endif
+ }
+ if (isochronous) {
+ isochronous = 0;
+ setEnhanced(mExtSettings);
+ setIsochronous(mExtSettings);
+ // The following are default values which
+ // may be overwritten during modal parsing
+ mExtSettings->mFPS = 60.0;
+ mExtSettings->mMean = 20000000.0;
+ mExtSettings->mVariance = 0.0;
+ mExtSettings->mBurstIPG = 5e-6;
+ if (optarg) {
+ mExtSettings->mIsochronousStr = new char[strlen(optarg) + 1 ];
+ strcpy(mExtSettings->mIsochronousStr, optarg);
+ }
+ }
+ if (burstipg) {
+ burstipg = 0;
+ setIPG(mExtSettings);
+ char *end;
+ mExtSettings->mBurstIPG = strtof(optarg,&end);
+ if (*end != '\0') {
+ fprintf (stderr, "ERRPORE: Invalid value of '%s' for --ipg\n", optarg);
+ exit(1);
+ }
+ }
+ if (rxwinclamp) {
+ rxwinclamp = 0;
+#if HAVE_DECL_TCP_WINDOW_CLAMP
+ mExtSettings->mClampSize = byte_atoi(optarg);
+ setRxClamp(mExtSettings);
+#else
+ fprintf(stderr, "--tcp-rx-window-clamp not supported on this platform\n");
+#endif
+ }
+ if (tcpwritetimes) {
+ tcpwritetimes = 0;
+ setTcpWriteTimes(mExtSettings);
+ }
+ if (notcpbbquickack) {
+ notcpbbquickack = 0;
+ notcpbbquickack_cliset = 1;
+ }
+ if (tcpquickack) {
+ tcpquickack = 0;
+#if HAVE_DECL_TCP_QUICKACK
+ setTcpQuickAck(mExtSettings);
+#endif
+ }
+ if (utctimes) {
+ setUTC(mExtSettings);
+ }
+ if (workingload) {
+ workingload = 0;
+#ifdef HAVE_THREAD
+ setWorkingLoadUp(mExtSettings);
+ setWorkingLoadDown(mExtSettings);
+ if (optarg) {
+ char *tmp= new char [strlen(optarg) + 1];
+ if (tmp) {
+ strcpy(tmp, optarg);
+ if ((results = strtok(tmp, ",")) != NULL) {
+ if (strcasecmp(results, "up") == 0) {
+ unsetWorkingLoadDown(mExtSettings);
+ } else if (strcasecmp(results, "down") == 0) {
+ unsetWorkingLoadUp(mExtSettings);
+ } else if (strcasecmp(results, "bidir") == 0) {
+ setWorkingLoadUp(mExtSettings);
+ setWorkingLoadDown(mExtSettings);
+ } else {
+ fprintf(stderr, "Unrecoginized value of %s for --working-load, use 'up', 'down' or 'bidir'\n", results);
+ }
+ if ((results = strtok(NULL, ",")) != NULL) {
+ mExtSettings->mWorkingLoadThreads = atoi(results);
+ }
+ delete [] tmp;
+ }
+ }
+ }
+#else
+ fprintf(stderr, "bounceback-congest option requires a platform that supports threads\n");
+#endif
+ }
+ if (bouncebackdelaystart) {
+ bouncebackdelaystart = 0;
+ char *end;
+ Timestamp holdbackdelay;
+ double delay = strtof(optarg, &end);
+ if (*end != '\0') {
+ fprintf (stderr, "Invalid value of '%s' for --bounceback-txdelay time\n", optarg);
+ } else {
+ holdbackdelay.set(delay);
+ mExtSettings->txholdback_timer.tv_sec = holdbackdelay.getSecs();
+ mExtSettings->txholdback_timer.tv_usec = (holdbackdelay.getUsecs());
+ setTxHoldback(mExtSettings);
+ }
+ }
+ if (txnotsentlowwater) {
+ txnotsentlowwater = 0;
+#if HAVE_DECL_TCP_NOTSENT_LOWAT
+ mExtSettings->mWritePrefetch = byte_atoi(optarg);
+ setWritePrefetch(mExtSettings);
+ setEnhanced(mExtSettings);
+#else
+ fprintf(stderr, "--tcp-write-prefetch not supported on this platform\n");
+#endif
+ }
+ if (burstperiodic) {
+ burstperiodic = 0;
+ setPeriodicBurst(mExtSettings);
+ if (optarg && (atof(optarg) > 1e-5)) { // limit to 10 usecs
+ mExtSettings->mFPS = 1.0/atof(optarg);
+ } else {
+ if (atof(optarg) != 0)
+ fprintf(stderr, "WARN: burst-period too small, must be greater than 10 usecs\n");
+ unsetPeriodicBurst(mExtSettings);
+ }
+ }
+ if (burstsize) {
+ burstsize = 0;
+ setPeriodicBurst(mExtSettings);
+ if (optarg) {
+ mExtSettings->mBurstSize = byte_atoi(optarg);
+ }
+ }
+ if (numreportstructs) {
+ numreportstructs = 0;
+ mExtSettings->numreportstructs = byte_atoi(optarg);
+ }
+ if (tapif) {
+ tapif = 0;
+#if HAVE_TUNTAP_TAP
+ if (optarg) {
+ mExtSettings->mIfrname = static_cast<char *>(calloc(strlen(optarg) + 1, sizeof(char)));
+ strcpy(mExtSettings->mIfrname, optarg);
+ }
+ setTapDev(mExtSettings);
+ setEnhanced(mExtSettings);
+ setL2LengthCheck(mExtSettings);
+#else
+ fprintf(stderr, "ERROR: tap devices not supported\n");
+ exit(1);
+#endif
+ }
+ if (tunif) {
+ tunif = 0;
+ fprintf(stderr, "ERROR: tun devices not yet supported\n");
+ exit(1);
+#if HAVE_TUNTAP_TUN
+ if (optarg) {
+ mExtSettings->mIfrname = static_cast<char *>(calloc(strlen(optarg) + 1, sizeof(char)));
+ strcpy(mExtSettings->mIfrname, optarg);
+ }
+ setTunDev(mExtSettings);
+ setEnhanced(mExtSettings);
+#endif
+ }
+ if (hideips) {
+ hideips = 0;
+ setHideIPs(mExtSettings);
+ }
+ if (bounceback) {
+ bounceback = 0;
+ setBounceBack(mExtSettings);
+ setNoDelay(mExtSettings);
+ setEnhanced(mExtSettings);
+ if (optarg) {
+ mExtSettings->mBounceBackBurst = atoi(optarg);
+ if (mExtSettings->mBounceBackBurst <= 0) {
+ fprintf(stderr, "WARN: invalid bounceback value, setting it to 10\n");
+ mExtSettings->mBounceBackBurst = 10;
+ }
+ } else {
+ mExtSettings->mBounceBackBurst = -1;
+ }
+ }
+ if (bouncebackhold) {
+ bouncebackhold = 0;
+ if (optarg)
+ //cli units is ms, working units is us
+ mExtSettings->mBounceBackHold = int(atof(optarg) * 1e3);
+ else
+ mExtSettings->mBounceBackHold = 0;
+ }
+ if (bouncebackperiod) {
+ bouncebackperiod = 0;
+ setPeriodicBurst(mExtSettings);
+ if (optarg) {
+ float value = atof(optarg);
+ if (value > 1e-5) { // limit to 10 usecs
+ mExtSettings->mFPS = 1.0/atof(optarg);
+ } else {
+ mExtSettings->mFPS = -1;
+ }
+ }
+ }
+ break;
+ default: // ignore unknown
+ break;
+ }
+} // end Interpret
+
+
+static void generate_permit_key (struct thread_Settings *mExtSettings) {
+ Timestamp now;
+ mExtSettings->mPermitKeyTime.tv_sec = now.getSecs();
+ mExtSettings->mPermitKeyTime.tv_usec = now.getUsecs();
+ int usecs = mExtSettings->mPermitKeyTime.tv_usec;
+ int timestrlength = snprintf(NULL, 0, "%ld.%06d-", static_cast<long>(mExtSettings->mPermitKeyTime.tv_sec), usecs);
+ snprintf(mExtSettings->mPermitKey, (timestrlength+1), "%ld.%06d-", static_cast<long>(mExtSettings->mPermitKeyTime.tv_sec), usecs);
+ srand(static_cast<unsigned int>(time(NULL)));
+ int index;
+ char characters[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
+ for(index = timestrlength; index < (DEFAULT_PERMITKEY_LEN + timestrlength); index++) {
+ int len = snprintf(mExtSettings->mPermitKey + index, sizeof(char) + 1, "%c", \
+ characters[rand() % (static_cast<int>(sizeof(characters)) - 1)]);
+ FAIL_errno ((len < 0), "permit-key gen", mExtSettings);
+ }
+ mExtSettings->mPermitKey[DEFAULT_PERMITKEY_LEN + timestrlength] = '\0';
+ if (strlen(mExtSettings->mPermitKey) > MAX_PERMITKEY_LEN) {
+ fprintf(stderr, "ERROR: permit key too long\n");
+ exit(1);
+ }
+}
+
+static void strip_v6_brackets (char *v6addr) {
+ char * results;
+ if (v6addr && (*v6addr == '[') && ((results = strtok(v6addr, "]")) != NULL)) {
+ int len = strlen(v6addr);
+ for (int jx = 0; jx < len; jx++) {
+ v6addr[jx]= v6addr[jx + 1];
+ }
+ }
+}
+
+static char * isv6_bracketed_port (char *v6addr) {
+ char *results = NULL;
+ if (v6addr && (*v6addr == '[') && ((results = strtok(v6addr, "]")) != NULL)) {
+ results = strtok(NULL, ":");
+ strip_v6_brackets(v6addr);
+ return results;
+ }
+ return NULL;
+}
+static char * isv4_port (char *v4addr) {
+ char *results = NULL;
+ if (((results = strtok(v4addr, ":")) != NULL) && ((results = strtok(NULL, ":")) != NULL)) {
+ return results;
+ }
+ return NULL;
+}
+
+
+// The commmand line options are position independent and hence some settings become "modal"
+// i.e. two passes are required to get all the final settings correct.
+// For example, -V indicates use IPv6 and -u indicates use UDP, and the default socket
+// read/write (UDP payload) size is different for ipv4 and ipv6.
+// So in the Settings_Interpret pass there is no guarantee to know all three of (-u and -V and not -l)
+// while parsing them individually.
+//
+// Since Settings_Interpret() will set all the *individual* options and flags
+// then the below code (per the example UDP, v4 or v6, and not -l) can set final
+// values, e.g. a correct default mBufLen. Other examples that need this are multicast
+// socket or not,-B local bind port parsing, and when to use the default UDP offered load
+//
+// Also apply the bail out or exit conditions if the user requested mutually exclusive
+// or incompatabile options
+void Settings_ModalOptions (struct thread_Settings *mExtSettings) {
+ char *results;
+ // Handle default read/write sizes based on v4, v6, UDP or TCP
+ if (!isBuflenSet(mExtSettings)) {
+ if (isUDP(mExtSettings)) {
+ if (isIPV6(mExtSettings) && mExtSettings->mThreadMode == kMode_Client) {
+ mExtSettings->mBufLen = kDefault_UDPBufLenV6;
+ } else {
+ mExtSettings->mBufLen = kDefault_UDPBufLen;
+ }
+ } else {
+ if (isBounceBack(mExtSettings))
+ mExtSettings->mBufLen = kDefault_BBTCPBufLen;
+ else
+ mExtSettings->mBufLen = kDefault_TCPBufLen;
+ }
+ }
+ if (!mExtSettings->mPortLast)
+ mExtSettings->mPortLast = mExtSettings->mPort;
+
+ // Handle default UDP offered load (TCP will be max, i.e. no read() or write() rate limiting)
+ if (!isBWSet(mExtSettings) && isUDP(mExtSettings)) {
+ mExtSettings->mAppRate = kDefault_UDPRate;
+ }
+ if (isTripTime(mExtSettings) && (isReverse(mExtSettings) || \
+ isFullDuplex(mExtSettings) || \
+ (mExtSettings->mMode != kTest_Normal))) {
+ setEnhanced(mExtSettings);
+ }
+ // Warnings
+ if (mExtSettings->mThreadMode == kMode_Client) {
+ if (isModeTime(mExtSettings) && infinitetime) {
+ unsetModeTime(mExtSettings);
+ setModeInfinite(mExtSettings);
+ fprintf(stderr, "WARNING: client will send traffic forever or until an external signal (e.g. SIGINT or SIGTERM) occurs to stop it\n");
+ }
+ if (isFullDuplex(mExtSettings) && isCongestionControl(mExtSettings)) {
+ fprintf(stderr, "WARNING: tcp congestion control will only be applied on transmit traffic, use -Z on the server\n");
+ }
+ }
+ if (isHideIPs(mExtSettings)) {
+ char hide_string[] = "(**hidden**)";
+ mExtSettings->mHideHost = new char[strlen(hide_string) + 1];
+ if (mExtSettings->mHideHost)
+ strcpy(mExtSettings->mHideHost, &hide_string[0]);
+ }
+
+ // Bail outs
+ bool bail = false;
+ // compat mode doesn't support these test settings
+ int compat_nosupport = (isReverse(mExtSettings) | isFullDuplex(mExtSettings) | isTripTime(mExtSettings) | isVaryLoad(mExtSettings) \
+ | isHistogram(mExtSettings) | isIsochronous(mExtSettings) \
+ | isEnhanced(mExtSettings) | (mExtSettings->mMode != kTest_Normal));
+ if (isCompat(mExtSettings) && compat_nosupport) {
+ fprintf(stderr, "ERROR: compatibility mode not supported with the requested with options\n");
+ bail = true;
+ }
+#if !(HAVE_DECL_IP_TOS)
+ if (isOverrideTOS(mExtSettings) || mExtSettings->mTOS) {
+ unsetOverrideTOS(mExtSettings);
+ fprintf(stderr, "WARN: IP_TOS not supported\n");
+ }
+#endif
+ if (isPermitKey(mExtSettings)) {
+ if (isUDP(mExtSettings)) {
+ fprintf(stderr, "ERROR: Option of --permit-key not supported with UDP\n");
+ bail = true;
+ } else if (mExtSettings->mPermitKey[0] != '\0') {
+ int keylen = strnlen(mExtSettings->mPermitKey, MAX_PERMITKEY_LEN + 1);
+ if (keylen < MIN_PERMITKEY_LEN) {
+ fprintf(stderr, "ERROR: value for --permit-key must have at least %d characters\n", MIN_PERMITKEY_LEN);
+ bail = true;
+ } else if (keylen > MAX_PERMITKEY_LEN) {
+ fprintf(stderr, "ERROR: value for --permit-key can't be more than %d characters \n", MAX_PERMITKEY_LEN);
+ bail = true;
+ }
+ }
+ }
+#if !HAVE_IPV6
+ if (isIPV6(mExtSettings)) {
+ fprintf(stderr, "ERROR: ipv6 not supported\n");
+ bail = true;
+ }
+#endif
+ if (isIPV4(mExtSettings) && isIPV6(mExtSettings)) {
+ fprintf(stderr, "WARN: both ipv4 and ipv6 set\n");
+ unsetIPV6(mExtSettings);
+ unsetIPV4(mExtSettings);
+ }
+ if (mExtSettings->mThreadMode == kMode_Client) {
+ if (isRemoveService(mExtSettings)) {
+ // -R on the client is overloaded and is the
+ // short option for --reverse, fix it now
+ setReverse(mExtSettings);
+ unsetRemoveService(mExtSettings);
+ }
+ if (isIncrSrcIP(mExtSettings) && (mExtSettings->mLocalhost==NULL)) {
+ fprintf(stderr, "ERROR: option of --incr-srcip requires -B bind option to be set\n");
+ bail = true;
+ }
+ if (isSumServerDstIP(mExtSettings)) {
+ fprintf(stderr, "WARN: option of --sum-dstip not supported on the client\n");
+ }
+ if (isOverrideTOS(mExtSettings)) {
+ unsetOverrideTOS(mExtSettings);
+ fprintf(stderr, "WARN: option of --tos-override not supported on the client\n");
+ }
+ if (isRxClamp(mExtSettings)) {
+ fprintf(stderr, "WARN: option of --tcp-rx-window-clamp not supported on the client\n");
+ unsetRxClamp(mExtSettings);
+ }
+ if (isJitterHistogram(mExtSettings)) {
+ fprintf(stderr, "WARN: option of --jitter-histogram not supported on the client\n");
+ unsetJitterHistogram(mExtSettings);
+ }
+ if (isIncrSrcPort(mExtSettings) && !mExtSettings->mBindPort) {
+ fprintf(stderr, "WARN: option of --incr-srcport requires -B bind option w/port to be set\n");
+ unsetIncrSrcPort(mExtSettings);
+ }
+ if (isPeriodicBurst(mExtSettings)) {
+ setEnhanced(mExtSettings);
+ setFrameInterval(mExtSettings);
+ }
+ if (mExtSettings->mTTL > MAXTTL) {
+ fprintf(stderr, "ERROR: option of -ttl (-T) must be less than %d\n", MAXTTL);
+ bail = true;
+ }
+ if (isPermitKey(mExtSettings) && (mExtSettings->mPermitKey[0] == '\0')) {
+ fprintf(stderr, "ERROR: option of --permit-key requires a value on the client\n");
+ bail = true;
+ }
+ if (!isUDP(mExtSettings) && isTxHoldback(mExtSettings) && isTxStartTime(mExtSettings)) {
+ fprintf(stderr,"ERROR: options of --txstart-time and --txdelay-time are mutually exclusive\n");
+ bail = true;
+ } else if (isTxStartTime(mExtSettings) || isTxHoldback(mExtSettings)) {
+ Timestamp now;
+ if (isTxStartTime(mExtSettings)) {
+ if (mExtSettings->txstart_epoch.tv_sec < 0) {
+ fprintf(stderr, "ERROR: --txstart-time must be a positive value\n");
+ unsetTxStartTime(mExtSettings);
+ bail = true;
+ } else if ((mExtSettings->txstart_epoch.tv_sec - now.getSecs()) > MAXDIFFTXSTART) {
+ char timestr[120];
+ struct timeval t;
+ t.tv_sec = now.getSecs();
+ t.tv_usec = now.getUsecs();
+ iperf_formattime(timestr, sizeof(timestr), t, isEnhanced(mExtSettings), isUTC(mExtSettings), YearThruSecTZ);
+ printf(error_starttime_exceeds, mExtSettings->txstart_epoch.tv_sec, mExtSettings->txstart_epoch.tv_usec, \
+ timestr, MAXDIFFTXSTART);
+ bail = true;
+ }
+ } else if (mExtSettings->txholdback_timer.tv_sec > MAXDIFFTXDELAY) {
+ printf(error_delaytime_exceeds, mExtSettings->txholdback_timer.tv_sec, MAXDIFFTXDELAY);
+ bail = true;
+ }
+ }
+ if (isBounceBack(mExtSettings)) {
+ if (static_cast<int> (mExtSettings->mBurstSize) > 0) {
+ fprintf(stderr, "WARN: options of --burst-size for bounce-back ignored, use -l sets size\n");
+ }
+ mExtSettings->mBounceBackBytes = mExtSettings->mBufLen;
+ mExtSettings->mBurstSize = mExtSettings->mBufLen;
+#if HAVE_DECL_TCP_QUICKACK
+ if (notcpbbquickack_cliset && isTcpQuickAck(mExtSettings)) {
+ fprintf(stderr, "ERROR: --tcp-quickack and --bounceback-no-quickack are mutually exclusive\n");
+ bail = true;
+ }
+ // be wary of double negatives here
+ if (!notcpbbquickack_cliset) {
+ setTcpQuickAck(mExtSettings);
+ }
+#endif
+ // default bounceback period to 1 second
+ if (!isPeriodicBurst(mExtSettings)) {
+ mExtSettings->mFPS = 1.0;
+ setPeriodicBurst(mExtSettings);
+ if (mExtSettings->mIntervalMode == kInterval_None) {
+ mExtSettings->mIntervalMode = kInterval_Time;
+ mExtSettings->mInterval = 1000000; // units is us
+ }
+ } else if (mExtSettings->mFPS <= 1e-5) {
+ unsetPeriodicBurst(mExtSettings);
+ }
+ if (isPeriodicBurst(mExtSettings) && (mExtSettings->mBounceBackBurst == -1)) {
+ mExtSettings->mBounceBackBurst = 10;
+ }
+ }
+#if HAVE_DECL_TCP_NOTSENT_LOWAT
+ if (isTripTime(mExtSettings) && !isUDP(mExtSettings)) {
+ if (isWritePrefetch(mExtSettings)) {
+ if (mExtSettings->mWritePrefetch <= 0) {
+ unsetWritePrefetch(mExtSettings);
+ }
+ } else {
+ mExtSettings->mWritePrefetch = SMALL_WRITE_PREFETCH;
+ setWritePrefetch(mExtSettings);
+ setEnhanced(mExtSettings);
+ }
+ }
+#endif
+ if (isPeriodicBurst(mExtSettings)) {
+ if (isIsochronous(mExtSettings)) {
+ fprintf(stderr, "ERROR: options of --burst-period and --isochronous cannot be applied together\n");
+ bail = true;
+ } else if (isNearCongest(mExtSettings)) {
+ fprintf(stderr, "ERROR: options of --burst-period and --near-congestion cannot be applied together\n");
+ bail = true;
+ } else if (static_cast<int> (mExtSettings->mBurstSize) == 0) {
+ mExtSettings->mBurstSize = byte_atoi("1M"); //default to 1 Mbyte
+ }
+ if (static_cast<int> (mExtSettings->mBurstSize) < mExtSettings->mBufLen) {
+ fprintf(stderr, "ERROR: option of --burst-size %d must be equal or larger to write length (-l) %d\n", mExtSettings->mBurstSize, mExtSettings->mBufLen);
+ bail = true;
+ }
+ } else if (!isBounceBack(mExtSettings) && (static_cast<int> (mExtSettings->mBurstSize) > 0)) {
+ setPeriodicBurst(mExtSettings);
+ mExtSettings->mFPS = 1.0;
+ fprintf(stderr, "WARN: option of --burst-size without --burst-period defaults --burst-period to 1 second\n");
+ }
+ if (isUDP(mExtSettings)) {
+ if (isPeerVerDetect(mExtSettings)) {
+ fprintf(stderr, "ERROR: option of -X or --peer-detect not supported with -u UDP\n");
+ bail = true;
+ }
+ if (isConnectOnly(mExtSettings)) {
+ fprintf(stderr, "ERROR: option of --connect-only not supported with -u UDP\n");
+ bail = true;
+ }
+ if (isBounceBack(mExtSettings)) {
+ fprintf(stderr, "ERROR: option of -X or --bounceback not supported with -u UDP\n");
+ bail = true;
+ }
+ if (isNearCongest(mExtSettings)) {
+ fprintf(stderr, "ERROR: option of --near-congestion not supported with -u UDP\n");
+ bail = true;
+ }
+ if (isIPG(mExtSettings) && isBWSet(mExtSettings)) {
+ fprintf(stderr, "ERROR: options of --b and --ipg cannot be applied together\n");
+ bail = true;
+ }
+ if (mExtSettings->mBurstIPG < 0.0) {
+ fprintf(stderr, "ERROR: option --ipg must be a positive value\n");
+ bail = true;
+ }
+ if (mExtSettings->mConnectRetries > 0) {
+ fprintf(stderr, "ERROR: option --connect-retries not supported with -u UDP\n");
+ bail = true;
+ }
+ if (isWritePrefetch(mExtSettings)) {
+ fprintf(stderr, "WARN: setting of option --tcp-write-prefetch is not supported with -u UDP\n");
+ unsetWritePrefetch(mExtSettings);
+ }
+ if (isTcpQuickAck(mExtSettings)) {
+ fprintf(stderr, "WARN: setting of option --tcp-quickack is not supported with -u UDP\n");
+ unsetWritePrefetch(mExtSettings);
+ }
+
+ {
+ double delay_target;
+ if (isIPG(mExtSettings)) {
+ delay_target = mExtSettings->mBurstIPG * 1e9; // convert from seconds to nanoseconds
+ } else {
+ // compute delay target in units of nanoseconds
+ if (mExtSettings->mAppRateUnits == kRate_BW) {
+ // compute delay for bandwidth restriction, constrained to [0,max] seconds
+ delay_target = (mExtSettings->mBufLen * 8e9) / mExtSettings->mAppRate;
+ } else {
+ delay_target = 1e9 / mExtSettings->mAppRate;
+ }
+ }
+ if (delay_target < 0 ||
+ delay_target > MAXIPGSECS * 1e9) {
+ fprintf(stderr, "ERROR: IPG delay target of %.1f secs too large (max value is %d secs)\n", (delay_target / 1e9), MAXIPGSECS);
+ bail = true;
+ }
+ }
+ if (isTripTime(mExtSettings)) {
+ if (mExtSettings->mBufLen < MINTRIPTIMEPLAYOAD) {
+ if (isReverse(mExtSettings) || isFullDuplex(mExtSettings) || (mExtSettings->mMode != kTest_Normal)) {
+ fprintf(stderr, "ERROR: payload (-l) size of %d too small for --trip-times, must be %d or greater\n", mExtSettings->mBufLen, MINTRIPTIMEPLAYOAD);
+ bail = true;
+ } else {
+ setSmallTripTime(mExtSettings);
+ }
+ }
+ }
+ } else {
+ if ((mExtSettings->mAppRate > 0) && isNearCongest(mExtSettings)) {
+ fprintf(stderr, "ERROR: option of --near-congestion and -b rate limited are mutually exclusive\n");
+ bail = true;
+ }
+ if (isBWSet(mExtSettings) && !(mExtSettings->mAppRateUnits == kRate_PPS) \
+ && ((mExtSettings->mAppRate / 8) < static_cast<uintmax_t>(mExtSettings->mBufLen))) {
+ fprintf(stderr, "ERROR: option -b and -l of %d are incompatible, consider setting -l to %d or lower\n", \
+ mExtSettings->mBufLen, static_cast<int>(mExtSettings->mAppRate / 8));
+ bail = true;
+ }
+ if (isIPG(mExtSettings)) {
+ fprintf(stderr, "ERROR: option --ipg requires -u UDP\n");
+ bail = true;
+ }
+ if (isNoUDPfin(mExtSettings)) {
+ fprintf(stderr, "ERROR: option --no-udp-fin requires -u UDP\n");
+ bail = true;
+ }
+ if (isTxHoldback(mExtSettings) && isConnectOnly(mExtSettings)) {
+ fprintf(stderr,"ERROR: Fail because --txdelay-time and --connect-only cannot be applied together\n");
+ bail = true; ;
+ }
+ if (mExtSettings->mAppRateUnits == kRate_PPS) {
+ fprintf(stderr, "ERROR: units of pps not supported with TCP\n");
+ bail = true;
+ }
+ if (isJitterHistogram(mExtSettings)) {
+ fprintf(stderr, "ERROR: option of --jitter-histogram not supported with TCP\n");
+ bail = true;
+ }
+ }
+ if (!isReverse(mExtSettings) && !isFullDuplex(mExtSettings) && isHistogram(mExtSettings)){
+ setTcpWriteTimes(mExtSettings);
+ mExtSettings->mHistBins = 100000; // 10 seconds wide
+ mExtSettings->mHistBinsize = 100; // 100 usec bins
+ mExtSettings->mHistUnits = 6; // usecs 10 pow(x)
+ mExtSettings->mHistci_lower = 5;
+ mExtSettings->mHistci_upper = 95;
+ }
+ if (isCongestionControl(mExtSettings) && isReverse(mExtSettings)) {
+ fprintf(stderr, "ERROR: tcp congestion control -Z and --reverse cannot be applied together\n");
+ bail = true;
+ }
+ {
+ int one_only = 0;
+ if (isFullDuplex(mExtSettings))
+ one_only++;
+ if (isReverse(mExtSettings))
+ one_only++;
+ if (mExtSettings->mMode != kTest_Normal)
+ one_only++;
+ if (one_only > 1) {
+ fprintf(stderr, "ERROR: options of --full-duplex, --reverse, -d and -r are mutually exclusive\n");
+ bail = true;
+ }
+ }
+ if (isBWSet(mExtSettings) && isIsochronous(mExtSettings)) {
+ fprintf(stderr, "ERROR: options of --b and --isochronous cannot be applied together\n");
+ bail = true;
+ }
+ } else {
+ if (mExtSettings->mPortLast && (!(mExtSettings->mPortLast >= mExtSettings->mPort))) {
+ fprintf(stderr, "ERROR: invalid port range of %d-%d\n",mExtSettings->mPort, mExtSettings->mPortLast);
+ bail = true;
+ }
+ if (isPermitKey(mExtSettings)) {
+ if (mExtSettings->mPermitKey[0]=='\0') {
+ generate_permit_key(mExtSettings);
+ }
+ if (mExtSettings->mListenerTimeout < 0) {
+ mExtSettings->mListenerTimeout = DEFAULT_PERMITKEY_LIFE;
+ }
+ }
+ if (isBounceBack(mExtSettings)) {
+ fprintf(stderr, "ERROR: setting of option --bounce-back is not supported on the server\n");
+ bail = true;
+ }
+ if (isTripTime(mExtSettings)) {
+ fprintf(stderr, "WARN: setting of option --trip-times is not supported on the server\n");
+ }
+ if (isWritePrefetch(mExtSettings)) {
+ fprintf(stderr, "WARN: setting of option --tcp-write-prefetch is not supported on the server\n");
+ unsetWritePrefetch(mExtSettings);
+ }
+ if (isIncrSrcIP(mExtSettings)) {
+ fprintf(stderr, "WARN: setting of option --incr-srcip is not supported on the server\n");
+ }
+ if (isVaryLoad(mExtSettings)) {
+ fprintf(stderr, "WARN: option of variance per -b is not supported on the server\n");
+ }
+ if (isTxStartTime(mExtSettings)) {
+ fprintf(stderr, "WARN: option of --txstart-time is not supported on the server\n");
+ }
+ if (isTxHoldback(mExtSettings)) {
+ fprintf(stderr, "WARN: option of --txdelay-time is not supported on the server\n");
+ }
+ if (isIPG(mExtSettings)) {
+ fprintf(stderr, "WARN: option of --ipg is not supported on the server\n");
+ }
+ if (isIsochronous(mExtSettings)) {
+ fprintf(stderr, "WARN: option of --isochronous is not supported on the server\n");
+ }
+ if (isFullDuplex(mExtSettings)) {
+ fprintf(stderr, "WARN: option of --full-duplex is not supported on the server\n");
+ }
+ if (isReverse(mExtSettings)) {
+ fprintf(stderr, "WARN: option of --reverse is not supported on the server\n");
+ }
+ if (isIncrDstIP(mExtSettings)) {
+ fprintf(stderr, "WARN: option of --incr-dstip is not supported on the server\n");
+ }
+ if (isFQPacing(mExtSettings)) {
+ fprintf(stderr, "WARN: option of --fq-rate is not supported on the server\n");
+ }
+ if (isNoUDPfin(mExtSettings)) {
+ fprintf(stderr, "WARN: option of --no-udp-fin is not supported on the server\n");
+ }
+ if (isPeerVerDetect(mExtSettings)) {
+ fprintf(stderr, "WARN: option of -X or --peer-detect not supported on the server\n");
+ }
+ if (mExtSettings->mConnectRetries > 0) {
+ fprintf(stderr, "WARN: option --connect-retries not supported on the server\n");
+ }
+ if (isNearCongest(mExtSettings)) {
+ fprintf(stderr, "WARN: option of --near-congestion not supported on the server\n");
+ }
+ if (isPeriodicBurst(mExtSettings)) {
+ fprintf(stderr, "WARN: option of --burst-period can only be set on the client\n");
+ }
+ if (mExtSettings->mBurstSize != 0) {
+ fprintf(stderr, "WARN: option of --burst-size not supported on the server\n");
+ }
+ if (isUDP(mExtSettings) && isRxClamp(mExtSettings)) {
+ fprintf(stderr, "WARN: option of --tcp-rx-window-clamp not supported using -u UDP \n");
+ unsetRxClamp(mExtSettings);
+ }
+ }
+ if (bail)
+ exit(1);
+
+ // Histogram optional settings
+ if (isHistogram(mExtSettings)) {
+ if (!mExtSettings->mHistogramStr) {
+ if (mExtSettings->mThreadMode == kMode_Server) {
+ // set default rx histogram settings, milliseconds bins between 0 and 1 secs
+ mExtSettings->mHistBins = 1000;
+ mExtSettings->mHistBinsize = 1;
+ mExtSettings->mHistUnits = 3;
+ mExtSettings->mHistci_lower = 5;
+ mExtSettings->mHistci_upper = 95;
+ } else {
+ // set default tx histogram settings, microseconds with 100 us bins
+ mExtSettings->mHistBins = 10000;
+ mExtSettings->mHistBinsize = 100;
+ mExtSettings->mHistUnits = 6;
+ mExtSettings->mHistci_lower = 5;
+ mExtSettings->mHistci_upper = 95;
+ }
+ } else {
+ // check for optional arguments to change histogram settings
+ if (((results = strtok(mExtSettings->mHistogramStr, ",")) != NULL) && !strcmp(results,mExtSettings->mHistogramStr)) {
+ // scan for unit specifier
+ char *tmp = new char [strlen(results) + 1];
+ strcpy(tmp, results);
+ if ((strtok(tmp, "u") != NULL) && strcmp(results,tmp) != 0) {
+ mExtSettings->mHistUnits = 6; // units is microseconds
+ } else {
+ strcpy(tmp, results);
+ if ((strtok(tmp, "m") != NULL) && strcmp(results,tmp) != 0) {
+ mExtSettings->mHistUnits = 3; // units is milliseconds
+ }
+ }
+ mExtSettings->mHistBinsize = atoi(tmp);
+ delete [] tmp;
+ mExtSettings->mHistci_lower = 5;
+ mExtSettings->mHistci_upper = 95;
+ if ((results = strtok(results+strlen(results)+1, ",")) != NULL) {
+ mExtSettings->mHistBins = byte_atoi(results);
+ if ((results = strtok(NULL, ",")) != NULL) {
+ mExtSettings->mHistci_lower = atof(results);
+ if ((results = strtok(NULL, ",")) != NULL) {
+ mExtSettings->mHistci_upper = atof(results);
+ }
+ }
+ }
+ }
+ }
+ }
+ if (isUDP(mExtSettings)) {
+ if (isFullDuplex(mExtSettings)) {
+ setNoUDPfin(mExtSettings);
+ }
+ if (mExtSettings->mThreadMode == kMode_Client) {
+ // L2 settings
+ if (l2checks && isUDP(mExtSettings)) {
+ l2checks = 0;
+#if defined(HAVE_LINUX_FILTER_H) && defined(HAVE_AF_PACKET)
+ // Request server to do length checks
+ setL2LengthCheck(mExtSettings);
+#else
+ fprintf(stderr, "WARNING: option --l2checks not supported on this platform\n");
+#endif
+ }
+ }
+ } else {
+ if (mExtSettings->mBurstSize && (static_cast<int>(mExtSettings->mBurstSize) < mExtSettings->mBufLen)) {
+ fprintf(stderr, "WARN: Setting --burst-size to %d because value given is smaller than -l value\n", \
+ mExtSettings->mBufLen);
+ mExtSettings->mBurstSize = mExtSettings->mBufLen;
+ } else if (isTripTime(mExtSettings) && !(mExtSettings->mBurstSize > 0)) {
+ mExtSettings->mBurstSize = mExtSettings->mBufLen;
+ }
+ }
+ if (isIsochronous(mExtSettings) && mExtSettings->mIsochronousStr) {
+ // parse client isochronous field,
+ // format is --isochronous <int>:<float>,<float> and supports
+ // human suffixes, e.g. --isochronous 60:100m,5m
+ // which is frames per second, mean and variance
+ if (mExtSettings->mThreadMode == kMode_Client) {
+ if (((results = strtok(mExtSettings->mIsochronousStr, ":")) != NULL) && !strcmp(results,mExtSettings->mIsochronousStr)) {
+ mExtSettings->mFPS = atof(results);
+ if ((results = strtok(NULL, ",")) != NULL) {
+ mExtSettings->mMean = bitorbyte_atof(results);
+ if (mExtSettings->mMean == 0.0) {
+ fprintf(stderr, "ERROR: Invalid --isochronous mean value, must be greater than zero\n");
+ exit(1);
+ }
+ if (!isUDP(mExtSettings)) {
+ if (mExtSettings->mMean < 0) {
+ fprintf(stderr, "ERROR: Invalid --isochronous units of 'p' with TCP must be bytes\n");
+ exit(1);
+ }
+ } else { // UDP
+ if (mExtSettings->mMean < 0) {
+ mExtSettings->mMean *= -8 * mExtSettings->mBufLen * mExtSettings->mFPS;
+ }
+ }
+ if ((results = strtok(NULL, ",")) != NULL) {
+ mExtSettings->mVariance = bitorbyte_atof(results);
+ }
+ } else {
+ mExtSettings->mMean = 20000000.0;
+ mExtSettings->mVariance = 0.0;
+ }
+ } else {
+ fprintf(stderr, "WARNING: Invalid --isochronous value, format is <fps>:<mean>,<variance> (e.g. 60:18M,1m)\n");
+ }
+ }
+ }
+
+ // Check for further mLocalhost (-B) and <dev> requests
+ // full addresses look like 192.168.1.1:6001%eth0 or [2001:e30:1401:2:d46e:b891:3082:b939]:6001%eth0
+ iperf_sockaddr tmp;
+ // Parse -B addresses
+ if (mExtSettings->mLocalhost) {
+ if (((results = strtok(mExtSettings->mLocalhost, "%")) != NULL) && ((results = strtok(NULL, "%")) != NULL)) {
+ mExtSettings->mIfrname = static_cast<char *>(calloc(strlen(results) + 1, sizeof(char)));
+ strcpy(mExtSettings->mIfrname, results);
+ if (mExtSettings->mThreadMode == kMode_Client) {
+ fprintf(stderr, "WARNING: Client cannot set bind device %s via -B consider using -c\n", mExtSettings->mIfrname);
+ free(mExtSettings->mIfrname);
+ mExtSettings->mIfrname = NULL;
+ }
+ }
+ if (isIPV6(mExtSettings)) {
+ results = isv6_bracketed_port(mExtSettings->mLocalhost);
+ } else {
+ results = isv4_port(mExtSettings->mLocalhost);
+ }
+ if (results) {
+ if (mExtSettings->mThreadMode == kMode_Client) {
+ mExtSettings->mBindPort = atoi(results);
+ } else {
+ fprintf(stderr, "WARNING: port %s ignored - set receive port on server via -p or -L\n", results);
+ }
+ }
+ // Check for multicast per the -B
+ SockAddr_setHostname(mExtSettings->mLocalhost, &tmp,
+ (isIPV6(mExtSettings) ? 1 : 0));
+ if ((mExtSettings->mThreadMode != kMode_Client) && SockAddr_isMulticast(&tmp)) {
+ setMulticast(mExtSettings);
+ } else if (SockAddr_isMulticast(&tmp)) {
+ if (mExtSettings->mIfrname) {
+ free(mExtSettings->mIfrname);
+ mExtSettings->mIfrname = NULL;
+ }
+ fprintf(stderr, "WARNING: Client src addr (per -B) must be ip unicast\n");
+ }
+ }
+ // Parse client (-c) addresses for multicast, link-local and bind to device, port incr
+ if (mExtSettings->mThreadMode == kMode_Client) {
+ if (mExtSettings->mPortLast > mExtSettings->mPort) {
+ int prcnt = ((mExtSettings->mPortLast - mExtSettings->mPort) + 1);
+ int threads_needed = (prcnt > mExtSettings->mThreads) ? prcnt : mExtSettings->mThreads;
+ if (mExtSettings->mThreads < prcnt) {
+ fprintf(stderr, "WARNING: port-range and -P mismatch (adjusting -P to %d)\n", threads_needed);
+ mExtSettings->mThreads = threads_needed;
+ } else if ((mExtSettings->mThreads > 1) && (threads_needed > prcnt)) {
+ fprintf(stderr, "WARNING: port-range and -P mismatch (adjusting port-range to %d-%d)\n", \
+ mExtSettings->mPort, mExtSettings->mPort + threads_needed);
+ }
+ }
+ mExtSettings->mIfrnametx = NULL; // default off SO_BINDTODEVICE
+ if (((results = strtok(mExtSettings->mHost, "%")) != NULL) && ((results = strtok(NULL, "%")) != NULL)) {
+ size_t len = strlen(results) + 1;
+ mExtSettings->mIfrnametx = static_cast<char *>(calloc(len, sizeof(char)));
+ strncpy(mExtSettings->mIfrnametx, results, len);
+ mExtSettings->mIfrnametx[len] = '\0';
+ }
+ if (isIPV6(mExtSettings))
+ strip_v6_brackets(mExtSettings->mHost);
+ // get the socket address settings from the host, needed for link-local and multicast tests
+ SockAddr_zeroAddress(&mExtSettings->peer);
+ SockAddr_remoteAddr(mExtSettings);
+ if (isIPV6(mExtSettings) && SockAddr_isLinklocal(&mExtSettings->peer)) {
+ // link-local doesn't use SO_BINDTODEVICE but includes it in the host string
+ // so stitch things back together and null the bind to name
+ if (mExtSettings->mIfrnametx) {
+ strcat(mExtSettings->mHost, "%");
+ strcat(mExtSettings->mHost, mExtSettings->mIfrnametx);
+ free(mExtSettings->mIfrnametx);
+ mExtSettings->mIfrnametx = NULL;
+ } else {
+ fprintf(stderr, "WARNING: usage of ipv6 link-local requires a device specifier, e.g. %s%%eth0\n", mExtSettings->mHost);
+ }
+ SockAddr_zeroAddress(&mExtSettings->peer); // for link-local, force getaddrinfo() in the connect
+ }
+ if (SockAddr_isMulticast(&mExtSettings->peer)) {
+ bail = false;
+ if ((mExtSettings->mThreads > 1) && !isIncrDstIP(mExtSettings)) {
+ fprintf(stderr, "ERROR: client option of -P greater than 1 not supported with multicast address\n");
+ bail = true;
+ } else if (isFullDuplex(mExtSettings) || isReverse(mExtSettings) || (mExtSettings->mMode != kTest_Normal)) {
+ fprintf(stderr, "ERROR: options of --full-duplex, --reverse, -d and -r not supported with multicast addresses\n");
+ bail = true;
+ }
+ if (bail)
+ exit(1);
+ else
+ setMulticast(mExtSettings);
+ }
+#ifndef HAVE_DECL_SO_BINDTODEVICE
+ if (mExtSettings->mIfrnametx) {
+ fprintf(stderr, "bind to device will be ignored because not supported\n");
+ free(mExtSettings->mIfrnametx);
+ mExtSettings->mIfrnametx=NULL;
+ }
+#endif
+ }
+ if ((mExtSettings->mIntervalMode == kInterval_Time) && (mExtSettings->mIntervalMode <= 0)) {
+ mExtSettings->mIntervalMode = kInterval_None;
+ mExtSettings->mInterval = 0;
+ }
+}
+
+void Settings_GetUpperCaseArg (const char *inarg, char *outarg) {
+ int len = strlen(inarg);
+ strcpy(outarg,inarg);
+
+ if ((len > 0) && (inarg[len-1] >='a')
+ && (inarg[len-1] <= 'z'))
+ outarg[len-1]= outarg[len-1]+'A'-'a';
+}
+
+void Settings_GetLowerCaseArg (const char *inarg, char *outarg) {
+ int len = strlen(inarg);
+ strcpy(outarg,inarg);
+
+ if ((len > 0) && (inarg[len-1] >='A')
+ && (inarg[len-1] <= 'Z'))
+ outarg[len-1]= outarg[len-1]-'A'+'a';
+}
+
+/*
+ * Settings_GenerateListenerSettings
+ * Called to generate the settings to be passed to the Listener
+ * instance that will handle dual testings from the client side
+ * this should only return an instance if it was called on
+ * the struct thread_settings instance generated from the command line
+ * for client side execution
+ */
+#define DUALTIMER_MS 300
+void Settings_GenerateListenerSettings (struct thread_Settings *client, struct thread_Settings **listener) {
+ if ((client->mMode == kTest_DualTest) || (client->mMode == kTest_TradeOff)) {
+ Settings_Copy(client, listener, SHALLOW_COPY);
+ unsetDaemon((*listener));
+ setCompat((*listener));
+ if (client->mListenPort != 0) {
+ (*listener)->mPort = client->mListenPort;
+ } else {
+ (*listener)->mPort = client->mPort;
+ }
+ if (client->mMode == kTest_TradeOff) {
+ (*listener)->mAmount = client->mAmount + DUALTIMER_MS;
+ } else if (client->mMode == kTest_DualTest) {
+ (*listener)->mAmount = client->mAmount + (SLOPSECS * 100);
+ }
+ if ((client->mMode != kTest_Normal) && ((*listener)->mAmount < DUALTIMER_MS)) {
+ (*listener)->mAmount = DUALTIMER_MS;
+ }
+ (*listener)->mFileName = NULL;
+ (*listener)->mHost = NULL;
+ (*listener)->mLocalhost = NULL;
+ (*listener)->mOutputFileName = NULL;
+ (*listener)->mMode = kTest_Normal;
+ (*listener)->mThreadMode = kMode_Listener;
+ if (client->mHost != NULL) {
+ (*listener)->mHost = new char[strlen(client->mHost) + 1];
+ strcpy((*listener)->mHost, client->mHost);
+ }
+ if (client->mLocalhost != NULL) {
+ (*listener)->mLocalhost = new char[strlen(client->mLocalhost) + 1];
+ strcpy((*listener)->mLocalhost, client->mLocalhost);
+ }
+ if (client->mBufLen <= 0) {
+ if (isUDP((*listener))) {
+ (*listener)->mBufLen = kDefault_UDPBufLen;
+ } else {
+ (*listener)->mBufLen = kDefault_TCPBufLen;
+ }
+ } else {
+ (*listener)->mBufLen = client->mBufLen;
+ }
+ setReport((*listener));
+ } else {
+ *listener = NULL;
+ }
+}
+
+void Settings_ReadClientSettingsIsoch (struct thread_Settings **client, struct client_hdrext_isoch_settings *hdr) {
+ (*client)->mFPS = ntohl(hdr->FPSl);
+ (*client)->mFPS += ntohl(hdr->FPSu) / static_cast<double>(rMillion);
+ (*client)->mMean = ntohl(hdr->Meanl);
+ (*client)->mMean += ntohl(hdr->Meanu) / static_cast<double>(rMillion);
+ (*client)->mVariance = ntohl(hdr->Variancel);
+ (*client)->mVariance += ntohl(hdr->Varianceu) / static_cast<double>(rMillion);
+ (*client)->mBurstIPG = ntohl(hdr->BurstIPGl);
+ (*client)->mBurstIPG += ntohl(hdr->BurstIPGu) / static_cast<double>(rMillion);
+}
+
+void Settings_ReadClientSettingsBurst (struct thread_Settings **client, struct client_hdrext_isoch_settings *hdr) {
+ (*client)->mFPS = ntohl(hdr->FPSl);
+ (*client)->mFPS += ntohl(hdr->FPSu) / static_cast<double>(rMillion);
+}
+
+void Settings_ReadClientSettingsV1 (struct thread_Settings **client, struct client_hdr_v1 *hdr) {
+ (*client)->mTID = thread_zeroid();
+ (*client)->mPort = static_cast<unsigned short>(ntohl(hdr->mPort));
+ (*client)->mThreads = 1;
+ struct thread_Settings *tmpSettings = *client;
+ if (hdr->mBufLen != 0) {
+ (*client)->mBufLen = ntohl(hdr->mBufLen);
+ setBuflenSet(tmpSettings);
+ } else {
+#ifdef DEFAULT_PAYLOAD_LEN_PER_MTU_DISCOVERY
+ checksock_max_udp_payload(tmpSettings);
+#endif
+ }
+ (*client)->mAmount = ntohl(hdr->mAmount);
+ if (((*client)->mAmount & 0x80000000) > 0) {
+ setModeTime((*client));
+#ifndef WIN32
+ (*client)->mAmount |= 0xFFFFFFFF00000000LL;
+#else
+ (*client)->mAmount |= 0xFFFFFFFF00000000;
+#endif
+ (*client)->mAmount = -(*client)->mAmount;
+ } else {
+ unsetModeTime((*client));
+ }
+}
+
+/*
+ * Settings_GenerateClientSettings
+ *
+ * Called by the Listener to generate the settings to be used by clients
+ * per things like dual tests. Set client pointer to null if a client isn't needed
+ *
+ * Note: mBuf should already be filled out per the Listener's apply_client_settings
+ */
+void Settings_GenerateClientSettings (struct thread_Settings *server, struct thread_Settings **client, void *mBuf) {
+ assert(server != NULL);
+ assert(mBuf != NULL);
+ uint32_t flags = isUDP(server) ? ntohl(*(uint32_t *)((char *)mBuf + sizeof(struct UDP_datagram))) : ntohl(*(uint32_t *)mBuf);
+ uint16_t upperflags = 0;
+ thread_Settings *reversed_thread = NULL;
+ *client = NULL;
+ bool v1test = (flags & HEADER_VERSION1) && !(flags & HEADER_VERSION2);
+#ifdef HAVE_THREAD_DEBUG
+ if (v1test)
+ thread_debug("header set for a version 1 test");
+#endif
+ if (isFullDuplex(server) || isServerReverse(server))
+ setTransferID(server, 1);
+ if (isFullDuplex(server) || v1test) {
+ Settings_Copy(server, client, SHALLOW_COPY);
+ reversed_thread = *client;
+ if (isFullDuplex(server) && !(flags & HEADER_VERSION1)) {
+ setFullDuplex(reversed_thread);
+ } else {
+ unsetFullDuplex(reversed_thread);
+ }
+ } else if (isServerReverse(server)) {
+ reversed_thread = server;
+ } else {
+ assert(0);
+ return;
+ }
+ reversed_thread->mThreadMode = kMode_Client;
+
+ if (isUDP(server)) { // UDP test information passed in every packet per being stateless
+ struct client_udp_testhdr *hdr = static_cast<struct client_udp_testhdr *>(mBuf);
+ Settings_ReadClientSettingsV1(&reversed_thread, &hdr->base);
+ if (isFullDuplex(server) || v1test) {
+ server->mAmount = reversed_thread->mAmount + (SLOPSECS * 100);
+ }
+ if (v1test) {
+ setServerReverse(reversed_thread);
+ if (flags & RUN_NOW) {
+ reversed_thread->mMode = kTest_DualTest;
+ } else {
+ reversed_thread->mMode = kTest_TradeOff;
+ }
+ }
+ if (flags & HEADER_EXTEND) {
+ reversed_thread->mAppRate = ntohl(hdr->extend.lRate);
+#ifdef HAVE_INT64_T
+ reversed_thread->mAppRate |= (static_cast<uint64_t>(ntohl(hdr->extend.uRate) >> 8) << 32);
+#endif
+ upperflags = ntohs(hdr->extend.upperflags);
+ if (upperflags & HEADER_NOUDPFIN) {
+ setNoUDPfin(reversed_thread);
+ }
+ if ((upperflags & HEADER_UNITS_PPS) == HEADER_UNITS_PPS) {
+ reversed_thread->mAppRateUnits = kRate_PPS;
+ } else {
+ reversed_thread->mAppRateUnits = kRate_BW;
+ }
+ reversed_thread->mTOS = ntohs(hdr->extend.tos);
+ if (isIsochronous(server)) {
+ Settings_ReadClientSettingsIsoch(&reversed_thread, &hdr->isoch_settings);
+ }
+ if (upperflags & HEADER_FQRATESET) {
+ setFQPacing(reversed_thread);
+ reversed_thread->mFQPacingRate = ntohl(hdr->start_fq.fqratel);
+#ifdef HAVE_INT64_T
+ reversed_thread->mFQPacingRate |= (static_cast<uint64_t>(ntohl(hdr->start_fq.fqrateu)) << 32);
+#endif
+ }
+ }
+ } else { //tcp first payload
+ struct client_tcp_testhdr *hdr = static_cast<struct client_tcp_testhdr *>(mBuf);
+ Settings_ReadClientSettingsV1(&reversed_thread, &hdr->base);
+
+ if (isFullDuplex(server) || v1test) {
+ server->mAmount = reversed_thread->mAmount + (SLOPSECS * 100);
+ }
+ if (v1test) {
+ setServerReverse(reversed_thread);
+ if (flags & RUN_NOW) {
+ reversed_thread->mMode = kTest_DualTest;
+ } else {
+ reversed_thread->mMode = kTest_TradeOff;
+ }
+ }
+ if (flags & HEADER_EXTEND) {
+ reversed_thread->mAppRate = ntohl(hdr->extend.lRate);
+#ifdef HAVE_INT64_T
+ reversed_thread->mAppRate |= (static_cast<uint64_t>(ntohl(hdr->extend.uRate) >> 8) << 32);
+#endif
+ upperflags = ntohs(hdr->extend.upperflags);
+ reversed_thread->mTOS = ntohs(hdr->extend.tos);
+
+ if (isIsochronous(server)) {
+ Settings_ReadClientSettingsIsoch(&reversed_thread, &hdr->isoch_settings);
+ } else if (isPeriodicBurst(server)) {
+ Settings_ReadClientSettingsBurst(&reversed_thread, &hdr->isoch_settings);
+ }
+ if (upperflags & HEADER_FQRATESET) {
+ setFQPacing(reversed_thread);
+ reversed_thread->mFQPacingRate = ntohl(hdr->start_fq.fqratel);
+#ifdef HAVE_INT64_T
+ reversed_thread->mFQPacingRate |= (static_cast<uint64_t>(ntohl(hdr->start_fq.fqrateu)) << 32);
+#endif
+ }
+ }
+ }
+ unsetTxHoldback(reversed_thread);
+ setNoSettReport(reversed_thread);
+ setNoConnectSync(reversed_thread);
+ // for legacy -d and -r need so set the reversed threads mHost
+ if (v1test) {
+ reversed_thread->mHost = new char[REPORT_ADDRLEN];
+ if ((reinterpret_cast<sockaddr*>(&server->peer))->sa_family == AF_INET) {
+ inet_ntop(AF_INET, &(reinterpret_cast<sockaddr_in*>(&server->peer))->sin_addr,
+ reversed_thread->mHost, REPORT_ADDRLEN);
+ }
+#if HAVE_IPV6
+ else {
+ inet_ntop(AF_INET6, &(reinterpret_cast<sockaddr_in6*>(&server->peer))->sin6_addr,
+ reversed_thread->mHost, REPORT_ADDRLEN);
+ }
+#endif
+ } else {
+ reversed_thread->mMode = kTest_Normal;
+#if HAVE_DECL_SO_MAX_PACING_RATE
+ if (isFQPacing(reversed_thread)) {
+ int rc = setsockopt(reversed_thread->mSock, SOL_SOCKET, SO_MAX_PACING_RATE, \
+ &reversed_thread->mFQPacingRate, sizeof(reversed_thread->mFQPacingRate));
+ WARN_errno(rc == SOCKET_ERROR, "setsockopt SO_MAX_PACING_RATE");
+ #ifdef HAVE_THREAD_DEBUG
+ #ifdef HAVE_INT64_T
+ thread_debug("Set socket %d pacing rate to %ld byte/sec", reversed_thread->mSock, reversed_thread->mFQPacingRate);
+ #else
+ thread_debug("Set socket %d pacing rate to %d byte/sec", reversed_thread->mSock, reversed_thread->mFQPacingRate);
+ #endif
+ #endif
+ }
+#endif // MAX_PACING_RATE
+ }
+}
+
+int Settings_GenerateClientHdrV1 (struct thread_Settings *client, struct client_hdr_v1 *hdr) {
+ if (isBuflenSet(client)) {
+ hdr->mBufLen = htonl(client->mBufLen);
+ } else {
+ hdr->mBufLen = 0;
+ }
+ if (client->mListenPort != 0) {
+ hdr->mPort = htonl(client->mListenPort);
+ } else {
+ hdr->mPort = htonl(client->mPort);
+ }
+ hdr->numThreads = htonl(client->mThreads);
+ if (isModeTime(client)) {
+ hdr->mAmount = htonl(-(long)client->mAmount);
+ } else {
+ hdr->mAmount = htonl((long)client->mAmount);
+ hdr->mAmount &= htonl(0x7FFFFFFF);
+ }
+ return (sizeof(struct client_hdr_v1));
+}
+
+/*
+ * Settings_GenerateClientHdr
+ *
+ * Called to generate the client header to be passed to the listener/server
+ *
+ * This will handle:
+ * o) dual testings from the listener/server side
+ * o) advanced udp test settings
+ *
+ * Returns size of header in bytes
+ */
+int Settings_GenerateClientHdr (struct thread_Settings *client, void *testhdr, struct timeval startTime) {
+ uint16_t len = 0;
+ uint16_t upperflags = 0;
+ uint16_t lowerflags = 0;
+ uint32_t flags = 0;
+
+ // Check the corner case of small packets and trip times
+ if (isUDP(client) && isSmallTripTime(client)) {
+ int buflen = (client->mBufLen < (int) sizeof(struct client_udpsmall_testhdr)) ? client->mBufLen \
+ : sizeof(struct client_udpsmall_testhdr);
+ struct client_udpsmall_testhdr *hdr = static_cast<struct client_udpsmall_testhdr *>(testhdr);
+ memset(hdr, 0, buflen);
+ hdr->flags = htons(HEADER16_SMALL_TRIPTIMES);
+#ifdef HAVE_THREAD_DEBUG
+ thread_debug("UDP small trip times flags = %X", ntohs(hdr->flags));
+#endif
+ return (MINIPERFPAYLOAD);
+ }
+ // flags common to both TCP and UDP
+ if (isReverse(client) && !isCompat(client)) {
+ upperflags |= HEADER_REVERSE;
+ if (isTxStartTime(client) && !TimeZero(startTime)) {
+ upperflags |= HEADER_EPOCH_START;
+ }
+ }
+ if (isFullDuplex(client) && !isCompat(client)) {
+ upperflags |= HEADER_FULLDUPLEX;
+ if (isTxStartTime(client) && !TimeZero(startTime)) {
+ upperflags |= HEADER_EPOCH_START;
+ }
+ }
+ if (isTxStartTime(client) && isTripTime(client) && !TimeZero(startTime)) {
+ upperflags |= HEADER_EPOCH_START;
+ }
+
+ // Now setup UDP and TCP specific passed settings from client to server
+ if (isUDP(client)) { // UDP test information passed in every packet per being stateless
+ struct client_udp_testhdr *hdr = static_cast<struct client_udp_testhdr *>(testhdr);
+ int buflen = (client->mBufLen < (int) sizeof(struct client_udp_testhdr)) ? client->mBufLen \
+ : sizeof(struct client_udp_testhdr);
+ memset(hdr, 0, buflen);
+ flags |= HEADER_SEQNO64B; // use 64 bit by default
+ flags |= HEADER_EXTEND;
+ hdr->extend.version_u = htonl(IPERF_VERSION_MAJORHEX);
+ hdr->extend.version_l = htonl(IPERF_VERSION_MINORHEX);
+ hdr->extend.tos = htons(client->mTOS & 0xFF);
+ if (isBWSet(client) && (client->mAppRate > 0)) {
+ uint64_t bw = 0;
+ if ((client->mAppRateUnits == kRate_PPS) && (isFullDuplex(client) || isReverse(client) || (client->mMode != kTest_Normal))) {
+ bw = client->mAppRate * 8 * client->mBufLen;
+ } else {
+ bw = client->mAppRate;
+ }
+ hdr->extend.lRate = htonl((uint32_t)(bw));
+#ifdef HAVE_INT64_T
+ hdr->extend.uRate = htonl(((uint32_t)(bw >> 32)) << 8);
+#endif
+ } else {
+ hdr->extend.lRate = htonl(kDefault_UDPRate);
+ hdr->extend.uRate = 0x0;
+ }
+ len += sizeof(struct client_hdrext);
+ len += Settings_GenerateClientHdrV1(client, &hdr->base);
+ if (!isCompat(client) && (client->mMode != kTest_Normal)) {
+ flags |= HEADER_VERSION1;
+ if (client->mMode == kTest_DualTest)
+ flags |= RUN_NOW;
+ hdr->base.flags = htonl(flags);
+ }
+ /*
+ * set the default offset where underlying "inline" subsystems can write into the udp payload
+ */
+ if (isL2LengthCheck(client)) {
+ flags |= HEADER_UDPTESTS;
+ if (isL2LengthCheck(client)) {
+ upperflags |= HEADER_L2LENCHECK;
+ if (isIPV6(client))
+ upperflags |= HEADER_L2ETHPIPV6;
+ }
+ }
+ if (isIsochronous(client)) {
+ flags |= (HEADER_UDPTESTS | HEADER_EXTEND) ;
+ upperflags |= HEADER_ISOCH;
+ if (isFullDuplex(client) || isReverse(client)) {
+ upperflags |= HEADER_ISOCH_SETTINGS;
+ hdr->isoch_settings.FPSl = htonl((long)(client->mFPS));
+ hdr->isoch_settings.FPSu = htonl((long)((client->mFPS - (long)(client->mFPS)) * rMillion));
+ hdr->isoch_settings.Meanl = htonl((long)(client->mMean));
+ hdr->isoch_settings.Meanu = htonl((long)(((client->mMean) - (long)(client->mMean)) * rMillion));
+ hdr->isoch_settings.Variancel = htonl((long)(client->mVariance));
+ hdr->isoch_settings.Varianceu = htonl((long)((client->mVariance - (long)(client->mVariance)) * rMillion));
+ hdr->isoch_settings.BurstIPGl = htonl((long)(client->mBurstIPG));
+ hdr->isoch_settings.BurstIPGu = htonl((long)((client->mBurstIPG - (long)(client->mBurstIPG)) * rMillion));
+ len += sizeof(struct client_hdrext_isoch_settings);
+ }
+ }
+ if (isReverse(client) || isFullDuplex(client)) {
+ flags |= (HEADER_UDPTESTS | HEADER_VERSION2);
+ }
+ if (isNoUDPfin(client)) {
+ flags |= (HEADER_UDPTESTS | HEADER_EXTEND);
+ upperflags |= HEADER_NOUDPFIN;
+ }
+ if (isTripTime(client) || isFQPacing(client) || isTxStartTime(client)) {
+ flags |= HEADER_UDPTESTS;
+ if (isTripTime(client) || isTxStartTime(client)) {
+ hdr->start_fq.start_tv_sec = htonl(startTime.tv_sec);
+ hdr->start_fq.start_tv_usec = htonl(startTime.tv_usec);
+ if (isTripTime(client))
+ upperflags |= HEADER_TRIPTIME;
+ }
+ if (isFQPacing(client)) {
+ upperflags |= HEADER_FQRATESET;
+ hdr->start_fq.fqratel = htonl((uint32_t) client->mFQPacingRate);
+#ifdef HAVE_INT64_T
+ hdr->start_fq.fqrateu = htonl((uint32_t) (client->mFQPacingRate >> 32));
+#endif
+ }
+ }
+ // Write flags to header so the listener can determine the tests requested
+ hdr->extend.upperflags = htons(upperflags);
+ hdr->extend.lowerflags = htons(lowerflags);
+
+ // isoch payload is an enclave field between v 0.13 and v0.14
+ // so figure out if it's there now - UDP only
+ // will be filled in by the client write
+ if (isTripTime(client) || isFQPacing(client) || isIsochronous(client))
+ len += sizeof (struct isoch_payload);
+
+ if (len > 0) {
+ len += sizeof(struct UDP_datagram);
+ flags |= ((len << 1) & HEADER_KEYLEN_MASK) | HEADER_LEN_BIT;
+ }
+ hdr->base.flags = htonl(flags);
+#ifdef HAVE_THREAD_DEBUG
+ thread_debug("UDPflags = %X", ntohl(hdr->base.flags));
+#endif
+ } else { // TCP first write with test information
+ struct client_tcp_testhdr *hdr = static_cast<struct client_tcp_testhdr *>(testhdr);
+#ifdef HAVE_THREAD_DEBUG
+ thread_debug("Client header init size %d (%p)", sizeof(struct client_tcp_testhdr), (void *) hdr);
+#endif
+ if (isBounceBack(client)) {
+ flags = HEADER_BOUNCEBACK;
+ len = sizeof(struct bounceback_hdr);
+ } else {
+ int buflen = (client->mBufLen < (int) sizeof(struct client_tcp_testhdr)) ? client->mBufLen \
+ : sizeof(struct client_tcp_testhdr);
+ memset(hdr, 0, buflen);
+ flags |= HEADER_EXTEND;
+ hdr->extend.version_u = htonl(IPERF_VERSION_MAJORHEX);
+ hdr->extend.version_l = htonl(IPERF_VERSION_MINORHEX);
+ hdr->extend.tos = htons(client->mTOS & 0xFF);
+ if (isBWSet(client)) {
+ hdr->extend.lRate = htonl((uint32_t)(client->mAppRate));
+#ifdef HAVE_INT64_T
+ hdr->extend.uRate = htonl(((uint32_t)(client->mAppRate >> 32)) << 8);
+#endif
+ }
+ len += sizeof(struct client_hdrext);
+ len += Settings_GenerateClientHdrV1(client, &hdr->base);
+ if (!isCompat(client) && (client->mMode != kTest_Normal)) {
+ flags |= HEADER_VERSION1;
+ if (client->mMode == kTest_DualTest)
+ flags |= RUN_NOW;
+ }
+ if (isPeerVerDetect(client)) {
+ flags |= (HEADER_V2PEERDETECT | HEADER_VERSION2);
+ }
+ if (isTripTime(client) || isFQPacing(client) || isIsochronous(client) || isTxStartTime(client)) {
+ hdr->start_fq.start_tv_sec = htonl(startTime.tv_sec);
+ hdr->start_fq.start_tv_usec = htonl(startTime.tv_usec);
+ hdr->start_fq.fqratel = htonl((uint32_t) client->mFQPacingRate);
+#ifdef HAVE_INT64_T
+ hdr->start_fq.fqrateu = htonl((uint32_t) (client->mFQPacingRate >> 32));
+#endif
+ len += sizeof(struct client_hdrext_starttime_fq);
+ // Set flags on
+ if (isTripTime(client)) {
+ upperflags |= HEADER_TRIPTIME;
+ }
+ if (isFQPacing(client)) {
+ upperflags |= HEADER_FQRATESET;
+ }
+ }
+#if HAVE_DECL_TCP_NOTSENT_LOWAT
+ if (isWritePrefetch(client) && (isReverse(client) || isFullDuplex(client))) {
+ upperflags |= HEADER_WRITEPREFETCH;
+ hdr->extend.TCPWritePrefetch = htonl((long)client->mWritePrefetch);
+ }
+#endif
+#if HAVE_DECL_TCP_QUICKACK
+ if (isTcpQuickAck(client) && (!isReverse(client) || isFullDuplex(client))) {
+ upperflags |= HEADER_TCPQUICKACK;
+ }
+#endif
+ if (isIsochronous(client) || isPeriodicBurst(client)) {
+ if (isPeriodicBurst(client)) {
+ upperflags |= HEADER_PERIODICBURST; // overload the isoch settings
+ } else {
+ upperflags |= HEADER_ISOCH;
+ }
+ if (isFullDuplex(client) || isReverse(client) || isPeriodicBurst(client)) {
+ hdr->isoch_settings.FPSl = htonl((long)client->mFPS);
+ hdr->isoch_settings.FPSu = htonl((long)((client->mFPS - (long)(client->mFPS)) * rMillion));
+ hdr->isoch_settings.Variancel = htonl((long)(client->mVariance));
+ hdr->isoch_settings.Varianceu = htonl((long)((client->mVariance - (long)(client->mVariance)) * rMillion));
+ if (!isPeriodicBurst(client)) {
+ hdr->isoch_settings.Meanl = htonl((long)(client->mMean));
+ hdr->isoch_settings.Meanu = htonl((long)(((client->mMean) - (long)(client->mMean)) * rMillion));
+ hdr->isoch_settings.BurstIPGl = htonl((long)client->mBurstIPG);
+ hdr->isoch_settings.BurstIPGu = htonl(((long)(client->mBurstIPG) - (long)client->mBurstIPG * rMillion));
+ } else {
+ hdr->isoch_settings.Meanl = htonl((long)(client->mBurstSize));
+ }
+ len += sizeof(struct client_hdrext_isoch_settings);
+ }
+ }
+ if (isReverse(client) || isFullDuplex(client)) {
+ flags |= HEADER_VERSION2;
+ }
+ hdr->extend.upperflags = htons(upperflags);
+ hdr->extend.lowerflags = htons(lowerflags);
+ if (len > 0) {
+ flags |= HEADER_LEN_BIT;
+ int keylen = 0;
+ if (!isServerReverse(client) && isPermitKey(client) && (client->mPermitKey[0] != '\0')) {
+ keylen = static_cast<int>(strnlen(client->mPermitKey, MAX_PERMITKEY_LEN));
+ flags |= HEADER_KEYCHECK;
+ struct permitKey *thiskey = reinterpret_cast<struct permitKey *>(static_cast<char *>(testhdr) + len);
+ thiskey->length = htons((uint16_t)keylen);
+ memcpy(thiskey->value, client->mPermitKey, keylen);
+ len += sizeof(thiskey->length);
+ }
+ flags |= ((len << 1) & HEADER_KEYLEN_MASK); // this is the key value offset passed to the server
+ len += keylen;
+ }
+ }
+ hdr->base.flags = htonl(flags);
+ }
+ return (len);
+}
+
+int Settings_ClientTestHdrLen (uint32_t flags, struct thread_Settings *inSettings) {
+ int peeklen = 0;
+ if ((flags & HEADER_VERSION1) || (flags & HEADER_VERSION2) || (flags & HEADER_EXTEND) || isPermitKey(inSettings)) {
+ if (flags & HEADER_LEN_BIT) {
+ peeklen = static_cast<int>((flags & HEADER_LEN_MASK) >> 1);
+ if (peeklen > MAX_HEADER_LEN) {
+ fprintf(stderr, "WARN: header of %d length too large\n", peeklen);
+ peeklen = -1;
+ }
+ } else {
+ if (flags & (HEADER_VERSION1 | HEADER_EXTEND)) {
+ peeklen = sizeof(struct client_hdr_v1);
+ }
+ if (flags & (HEADER_VERSION2 | HEADER_EXTEND)) {
+ peeklen += sizeof(struct client_hdrext);
+ }
+ }
+ }
+ return peeklen;
+}
diff --git a/src/SocketAddr.c b/src/SocketAddr.c
new file mode 100644
index 0000000..4322475
--- /dev/null
+++ b/src/SocketAddr.c
@@ -0,0 +1,941 @@
+/*---------------------------------------------------------------
+ * Copyright (c) 1999,2000,2001,2002,2003
+ * The Board of Trustees of the University of Illinois
+ * All Rights Reserved.
+ *---------------------------------------------------------------
+ * 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.
+ * ________________________________________________________________
+ * National Laboratory for Applied Network Research
+ * National Center for Supercomputing Applications
+ * University of Illinois at Urbana-Champaign
+ * http://www.ncsa.uiuc.edu
+ * ________________________________________________________________
+ *
+ * Socket.cpp
+ * by Ajay Tirumala <tirumala@ncsa.uiuc.edu>
+ * and Mark Gates <mgates@nlanr.net>
+ * ------------------------------------------------------------------- */
+
+#define HEADERS()
+
+#include "headers.h"
+
+#include "SocketAddr.h"
+#ifdef HAVE_IFADDRS_H
+#include <ifaddrs.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* -------------------------------------------------------------------
+ * Create a socket address. If inHostname is not null, resolve that
+ * address and fill it in. Fill in the port number. Use IPv6 ADDR_ANY
+ * if that is what is desired.
+ * ------------------------------------------------------------------- */
+void SockAddr_remoteAddr (struct thread_Settings *inSettings) {
+ if (SockAddr_isZeroAddress(&inSettings->peer) == 0) {
+ if (inSettings->mHost != NULL) {
+ SockAddr_setHostname(inSettings->mHost, &inSettings->peer, isIPV6(inSettings));
+ if (inSettings->incrdstip)
+ SockAddr_incrAddress(&inSettings->peer, inSettings->incrdstip);
+ } else {
+#if HAVE_IPV6
+ if (isIPV6(inSettings)) {
+ ((struct sockaddr*)&inSettings->peer)->sa_family = AF_INET6;
+ } else {
+ ((struct sockaddr*)&inSettings->peer)->sa_family = AF_INET;
+ }
+ }
+ if (SockAddr_isIPv6(&inSettings->peer)) {
+ inSettings->size_peer = sizeof(struct sockaddr_in6);
+ } else {
+ inSettings->size_peer = sizeof(struct sockaddr_in);
+ }
+#else
+ }
+ ((struct sockaddr*)&inSettings->peer)->sa_family = AF_INET;
+ inSettings->size_peer = sizeof(struct sockaddr_in);
+#endif
+ SockAddr_setPort(&inSettings->peer, inSettings->mPort);
+ }
+}
+// end SocketAddr
+
+void SockAddr_localAddr (struct thread_Settings *inSettings) {
+ SockAddr_zeroAddress(&inSettings->local);
+
+ if (inSettings->mLocalhost != NULL) {
+ SockAddr_setHostname(inSettings->mLocalhost, &inSettings->local,
+ isIPV6(inSettings));
+ if (inSettings->incrsrcip)
+ SockAddr_incrAddress(&inSettings->local, inSettings->incrsrcip);
+
+ } else {
+#if HAVE_IPV6
+ if (isIPV6(inSettings)) {
+ ((struct sockaddr*)&inSettings->local)->sa_family = AF_INET6;
+ } else {
+ ((struct sockaddr*)&inSettings->local)->sa_family = AF_INET;
+ }
+ }
+
+ if (SockAddr_isIPv6(&inSettings->local)) {
+ inSettings->size_local = sizeof(struct sockaddr_in6);
+ } else {
+ inSettings->size_local = sizeof(struct sockaddr_in);
+ }
+#else
+ ((struct sockaddr*)&inSettings->local)->sa_family = AF_INET;
+ }
+ inSettings->size_local = sizeof(struct sockaddr_in);
+#endif
+ /*
+ * This section handles the *local* port binding (which is messy)
+ * Quintuple is Proto:LocalIP:LocalPort:DstIP:DstPort
+ *
+ * There are three threads being Client, Listener and Server
+ * mPort comes from the -p command (which defaults to 5001)
+ * mLocalhost indicates -B set requesting a local binding
+ * mBindPort comes from -B IP:<port> (where port defaults to 0)
+ * Multicast IP address, e.g. 239.1.1.1, is set per a -B
+ * Zero will cause the OS to auto assign a LocalPort
+ * For iperf -s; Windows uses listener thread, *nix a server thread
+ * (so, effectively, Listener and Server threads are the same)
+ * Client threads support either auto assignment (default) or
+ * user specified (via -B)
+ */
+ if (inSettings->mLocalhost == NULL) {
+ if (inSettings->mThreadMode == kMode_Client) {
+ /*
+ * Client thread, -p and no -B,
+ * OS will auto assign a free local port
+ */
+ SockAddr_setPortAny (&inSettings->local);
+ } else {
+ /* Server or Listener thread, -p and no -B */
+ SockAddr_setPort(&inSettings->local, inSettings->mPort);
+ }
+ } else {
+ // -B was set
+ if (inSettings->mThreadMode == kMode_Client) {
+ /* Client thread */
+ if (inSettings->mBindPort) {
+ /*
+ * User specified port so use it
+ */
+#if HAVE_DECL_SO_REUSEPORT
+ int boolean = 1;
+ Socklen_t len = sizeof(boolean);
+ setsockopt(inSettings->mSock, SOL_SOCKET, SO_REUSEPORT, (char*) &boolean, len);
+#endif
+ SockAddr_setPort(&inSettings->local, (inSettings->mBindPort + inSettings->incrsrcport));
+ } else {
+ /*
+ * No user specified port, let OS assign a free one
+ */
+ SockAddr_setPortAny (&inSettings->local);
+ }
+ } else {
+ /*
+ * Server or Listener thread, both always use -p port
+ * any -B port will be ignored
+ */
+ SockAddr_setPort(&inSettings->local, inSettings->mPort);
+ }
+ }
+}
+// end SocketAddr
+
+/* -------------------------------------------------------------------
+ * Resolve the hostname address and fill it in.
+ * ------------------------------------------------------------------- */
+void SockAddr_setHostname (const char* inHostname, iperf_sockaddr *inSockAddr, int isIPv6) {
+ // ..I think this works for both ipv6 & ipv4... we'll see
+ bool found = false;
+ int ret_ga;
+ struct addrinfo *res = NULL, *itr;
+ struct addrinfo hints;
+ memset(&hints, 0, sizeof(hints));
+ if (!isIPv6) {
+ hints.ai_family = AF_INET;
+ ret_ga = getaddrinfo(inHostname, NULL, &hints, &res);
+ if (ret_ga == 0) {
+ if (res && res->ai_addr) {
+ itr = res;
+ // Now search for a IPv4 Address
+ while (itr != NULL) {
+ if (itr->ai_family == AF_INET) {
+ memcpy(inSockAddr, (itr->ai_addr), (itr->ai_addrlen));
+ freeaddrinfo(res);
+ found = true;
+ break;
+ } else {
+ itr = itr->ai_next;
+ }
+ }
+ }
+ }
+ }
+#if HAVE_IPV6
+ if (!found) {
+ hints.ai_family = AF_INET6;
+ ret_ga = getaddrinfo(inHostname, NULL, &hints, &res);
+ if (ret_ga == 0) {
+ if (res && res->ai_addr) {
+ // Now search for a IPv6 Address
+ itr = res;
+ while (itr != NULL) {
+ if (itr->ai_family == AF_INET6) {
+ memcpy(inSockAddr, (itr->ai_addr), (itr->ai_addrlen));
+ freeaddrinfo(res);
+ found = true;
+ break;
+ } else {
+ itr = itr->ai_next;
+ }
+ }
+ }
+ }
+ }
+#endif // IPV6
+ // getaddrinfo didn't find an address, fallback to gethostbyname for v4
+ if (!found && !isIPv6) {
+ // first try just converting dotted decimal
+ // on Windows gethostbyname doesn't understand dotted decimal
+ struct sockaddr_in *sockaddr = (struct sockaddr_in *)inSockAddr;
+ int rc = inet_pton(AF_INET, inHostname, &sockaddr->sin_addr);
+ sockaddr->sin_family = AF_INET;
+ if (rc == 0) {
+ struct hostent *hostP = gethostbyname(inHostname);
+ if (hostP == NULL) {
+ /* this is the same as herror() but works on more systems */
+ const char* format;
+ switch (h_errno) {
+ case HOST_NOT_FOUND:
+ format = "%s: Unknown host\n";
+ break;
+ case NO_ADDRESS:
+ format = "%s: No address associated with name\n";
+ break;
+ case NO_RECOVERY:
+ format = "%s: Unknown server error\n";
+ break;
+ case TRY_AGAIN:
+ format = "%s: Host name lookup failure\n";
+ break;
+ default:
+ format = "%s: Unknown resolver error\n";
+ break;
+ }
+ fprintf(stderr, format, inHostname);
+ exit(1);
+ return; // TODO throw
+ }
+ found = true;
+ memcpy(&sockaddr->sin_addr, *(hostP->h_addr_list), (hostP->h_length));
+ }
+ }
+ if (!found) {
+ fprintf(stderr, "ERROR: failed to find an ip address for host '%s'\n", inHostname);
+ exit(1);
+ }
+}
+// end setHostname
+
+/* -------------------------------------------------------------------
+ * Copy the IP address into the string.
+ * ------------------------------------------------------------------- */
+void SockAddr_getHostAddress (iperf_sockaddr *inSockAddr, char* outAddress,
+ size_t len) {
+ if (((struct sockaddr*)inSockAddr)->sa_family == AF_INET) {
+ inet_ntop(AF_INET, &(((struct sockaddr_in*) inSockAddr)->sin_addr),
+ outAddress, len);
+ }
+#if HAVE_IPV6
+ else {
+ inet_ntop(AF_INET6, &(((struct sockaddr_in6*) inSockAddr)->sin6_addr),
+ outAddress, len);
+ }
+#endif
+}
+// end getHostAddress
+
+/* -------------------------------------------------------------------
+ * Set the address to any (generally all zeros).
+ * ------------------------------------------------------------------- */
+void SockAddr_setAddressAny (iperf_sockaddr *inSockAddr) {
+ if (((struct sockaddr*)inSockAddr)->sa_family == AF_INET)
+ memset(&(((struct sockaddr_in*) inSockAddr)->sin_addr), 0,
+ sizeof(struct in_addr));
+#if HAVE_IPV6
+ else
+ memset(&(((struct sockaddr_in6*) inSockAddr)->sin6_addr), 0,
+ sizeof(struct in6_addr));
+#endif
+}
+// end setAddressAny
+
+/* -------------------------------------------------------------------
+ * Incr the address by value
+ * ------------------------------------------------------------------- */
+void SockAddr_incrAddress (iperf_sockaddr *inSockAddr, int value) {
+ if (((struct sockaddr*)inSockAddr)->sa_family == AF_INET)
+ ((struct sockaddr_in *)inSockAddr)->sin_addr.s_addr += htonl(value);
+#if HAVE_IPV6
+ else {
+ uint32_t *lower = (uint32_t *)&((struct sockaddr_in6 *)inSockAddr)->sin6_addr.s6_addr[12];
+ *lower += htonl(value);
+ }
+#endif
+}
+// end setAddressAny
+
+
+/* -------------------------------------------------------------------
+ * Set the port to the given port. Handles the byte swapping.
+ * ------------------------------------------------------------------- */
+void SockAddr_setPort (iperf_sockaddr *inSockAddr, unsigned short inPort) {
+ if (((struct sockaddr*)inSockAddr)->sa_family == AF_INET)
+ ((struct sockaddr_in*) inSockAddr)->sin_port = htons(inPort);
+#if HAVE_IPV6
+ else
+ ((struct sockaddr_in6*) inSockAddr)->sin6_port = htons(inPort);
+#endif
+
+}
+// end setPort
+
+/* -------------------------------------------------------------------
+ * Set the port to zero, which lets the OS pick the port.
+ * ------------------------------------------------------------------- */
+void SockAddr_setPortAny (iperf_sockaddr *inSockAddr) {
+ SockAddr_setPort(inSockAddr, 0);
+}
+// end setPortAny
+
+/* -------------------------------------------------------------------
+ * Return the port. Handles the byte swapping.
+ * ------------------------------------------------------------------- */
+unsigned short SockAddr_getPort (iperf_sockaddr *inSockAddr) {
+ if (((struct sockaddr*)inSockAddr)->sa_family == AF_INET)
+ return ntohs(((struct sockaddr_in*) inSockAddr)->sin_port);
+#if HAVE_IPV6
+ else
+ return ntohs(((struct sockaddr_in6*) inSockAddr)->sin6_port);
+#endif
+ return 0;
+
+}
+// end getPort
+
+/* -------------------------------------------------------------------
+ * Return the IPv4 Internet Address from the sockaddr_in structure
+ * ------------------------------------------------------------------- */
+struct in_addr* SockAddr_get_in_addr (iperf_sockaddr *inSockAddr) {
+ if (((struct sockaddr*)inSockAddr)->sa_family == AF_INET)
+ return &(((struct sockaddr_in*) inSockAddr)->sin_addr);
+
+ fprintf(stderr, "FATAL: get_in_addr called on IPv6 address\n");
+ return NULL;
+}
+
+/* -------------------------------------------------------------------
+ * Return the IPv6 Internet Address from the sockaddr_in6 structure
+ * ------------------------------------------------------------------- */
+#if HAVE_IPV6
+struct in6_addr* SockAddr_get_in6_addr (iperf_sockaddr *inSockAddr) {
+ if (((struct sockaddr*)inSockAddr)->sa_family == AF_INET6)
+ return &(((struct sockaddr_in6*) inSockAddr)->sin6_addr);
+
+ fprintf(stderr, "FATAL: get_in6_addr called on IPv4 address\n");
+ return NULL;
+}
+#endif
+
+
+/* -------------------------------------------------------------------
+ * Return the size of the appropriate address structure.
+ * ------------------------------------------------------------------- */
+Socklen_t SockAddr_get_sizeof_sockaddr (iperf_sockaddr *inSockAddr) {
+#if HAVE_IPV6
+ if (((struct sockaddr*)inSockAddr)->sa_family == AF_INET6) {
+ return(sizeof(struct sockaddr_in6));
+ }
+#endif
+ return(sizeof(struct sockaddr_in));
+}
+// end get_sizeof_sockaddr
+
+
+/* -------------------------------------------------------------------
+ * Return if IPv6 socket
+ * ------------------------------------------------------------------- */
+int SockAddr_isIPv6 (iperf_sockaddr *inSockAddr) {
+#if HAVE_IPV6
+ if (((struct sockaddr*)inSockAddr)->sa_family == AF_INET6) {
+ return 1;
+ }
+#endif
+ return 0;
+}
+// end get_sizeof_sockaddr
+
+/* -------------------------------------------------------------------
+ * Return true if the address is multicast ip address.
+ * ------------------------------------------------------------------- */
+int SockAddr_isMulticast (iperf_sockaddr *inSockAddr) {
+#if HAVE_IPV6
+ if (((struct sockaddr*)inSockAddr)->sa_family == AF_INET6) {
+ return(IN6_IS_ADDR_MULTICAST(&(((struct sockaddr_in6*) inSockAddr)->sin6_addr)));
+ } else
+#endif
+ {
+
+#ifdef IN_MULTICAST
+ // 224.0.0.0 to 239.255.255.255 (e0.00.00.00 to ef.ff.ff.ff)
+ // convert from network to host byte order
+ uint32_t maddr = ntohl((uint32_t)(((struct sockaddr_in*) inSockAddr)->sin_addr.s_addr));
+ return (IN_MULTICAST(maddr));
+#else
+ const unsigned long kClassD_Mask = 0xf0000000L;
+ const unsigned long kMulticast = 0xe0000000L;
+ return((((ntohl(((struct sockaddr_in*) inSockAddr)->sin_addr.s_addr)) & kClassD_Mask) == kMulticast));
+#endif
+ }
+}
+// end isMulticast
+
+/* -------------------------------------------------------------------
+ * Return true if the address is multicast ip address.
+ * ------------------------------------------------------------------- */
+int SockAddr_isLinklocal (iperf_sockaddr *inSockAddr) {
+#if HAVE_IPV6
+ if (((struct sockaddr*)inSockAddr)->sa_family == AF_INET6) {
+ return(IN6_IS_ADDR_LINKLOCAL(&(((struct sockaddr_in6*) inSockAddr)->sin6_addr)));
+ } else
+#endif
+ {
+ return 0;
+ }
+}
+
+/* -------------------------------------------------------------------
+ * Zero out the address structure.
+ * ------------------------------------------------------------------- */
+void SockAddr_zeroAddress (iperf_sockaddr *inSockAddr) {
+ memset(inSockAddr, 0, sizeof(iperf_sockaddr));
+}
+
+int SockAddr_isZeroAddress (iperf_sockaddr *inSockAddr) {
+ iperf_sockaddr zeroSockAddr;
+ memset(&zeroSockAddr, 0, sizeof(iperf_sockaddr));
+ return(memcmp((void *)inSockAddr, (void *)&zeroSockAddr, sizeof(iperf_sockaddr)));
+}
+
+/* -------------------------------------------------------------------
+ * Compare two sockaddrs and return true if they are equal
+ * ------------------------------------------------------------------- */
+int SockAddr_are_Equal (iperf_sockaddr *first, iperf_sockaddr *second) {
+ if (((struct sockaddr*)first)->sa_family == AF_INET && ((struct sockaddr*)second)->sa_family == AF_INET) {
+ // compare IPv4 adresses
+ return(((long) ((struct sockaddr_in*)first)->sin_addr.s_addr == (long) ((struct sockaddr_in*)second)->sin_addr.s_addr)
+ && (((struct sockaddr_in*)first)->sin_port == ((struct sockaddr_in*)second)->sin_port));
+ }
+#if HAVE_IPV6
+ if (((struct sockaddr*)first)->sa_family == AF_INET6 && ((struct sockaddr*)second)->sa_family == AF_INET6) {
+ // compare IPv6 addresses
+ return(!memcmp(((struct sockaddr_in6*)first)->sin6_addr.s6_addr, ((struct sockaddr_in6*)second)->sin6_addr.s6_addr, sizeof(struct in6_addr))
+ && (((struct sockaddr_in6*)first)->sin6_port == ((struct sockaddr_in6*)second)->sin6_port));
+ }
+#endif
+ return 0;
+}
+
+/* -------------------------------------------------------------------
+ * Compare two sockaddrs and return true if the hosts are equal
+ * ------------------------------------------------------------------- */
+int SockAddr_Hostare_Equal (iperf_sockaddr* first, iperf_sockaddr *second) {
+ if (((struct sockaddr*)first)->sa_family == AF_INET && ((struct sockaddr*)second)->sa_family == AF_INET) {
+ // compare IPv4 adresses
+ return((long) ((struct sockaddr_in*)first)->sin_addr.s_addr ==
+ (long) ((struct sockaddr_in*)second)->sin_addr.s_addr);
+ }
+#if HAVE_IPV6
+ if (((struct sockaddr*)first)->sa_family == AF_INET6 && ((struct sockaddr*)second)->sa_family == AF_INET6) {
+ // compare IPv6 addresses
+ return(!memcmp(((struct sockaddr_in6*)first)->sin6_addr.s6_addr,
+ ((struct sockaddr_in6*)second)->sin6_addr.s6_addr, sizeof(struct in6_addr)));
+ }
+#endif
+ return 0;
+}
+
+/* -------------------------------------------------------------------
+ * Find the interface name of a connected socket (when not already set)
+ * Can be forced with -B <ip>%<name> (server), -c <ip>%<name> (client)
+ * Note that kernel maps, e.g. via routing tables, to the actual device
+ * so these can change. Assume they won't change during the life
+ * of a thread.
+ *
+ * Store (and cache) the results in the thread settings structure
+ * Return 0 if set, -1 if not
+ * ------------------------------------------------------------------- */
+int SockAddr_Ifrname (struct thread_Settings *inSettings) {
+#ifdef HAVE_IFADDRS_H
+ if (inSettings->mIfrname == NULL) {
+ struct sockaddr_storage myaddr;
+ struct ifaddrs* ifaddr;
+ struct ifaddrs* ifa;
+ socklen_t addr_len;
+ addr_len = sizeof(struct sockaddr_storage);
+ getsockname(inSettings->mSock, (struct sockaddr*)&myaddr, &addr_len);
+ getifaddrs(&ifaddr);
+
+ // look which interface contains the desired IP per getsockname() which sets myaddr
+ // When found, ifa->ifa_name contains the name of the interface (eth0, eth1, ppp0...)
+ if (myaddr.ss_family == AF_INET) {
+ // v4 socket family (supports v4 only)
+ struct sockaddr_in* addr = (struct sockaddr_in*)&myaddr;
+ for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
+ if ((ifa->ifa_addr) && (ifa->ifa_addr->sa_family == AF_INET)) {
+ struct sockaddr_in* inaddr = (struct sockaddr_in*)ifa->ifa_addr;
+ if ((inaddr->sin_addr.s_addr == addr->sin_addr.s_addr) && (ifa->ifa_name)) {
+ // Found v4 address in v4 addr family, copy it to thread settings structure
+ inSettings->mIfrname = calloc (strlen(ifa->ifa_name) + 1, sizeof(char));
+ strcpy(inSettings->mIfrname, ifa->ifa_name);
+ break;
+ }
+ }
+ }
+ } else if (myaddr.ss_family == AF_INET6) {
+ // v6 socket family (supports both v4 and v6)
+ struct sockaddr_in6* addr = (struct sockaddr_in6*)&myaddr;
+ // Link local address are shared amongst all devices
+ // Try to pull the interface from the destination
+ if ((inSettings->mThreadMode == kMode_Client) && (IN6_IS_ADDR_LINKLOCAL(&addr->sin6_addr))) {
+ char *results;
+ char *copy = (char *)malloc(strlen(inSettings->mHost)+1);
+ strcpy(copy,(const char *)inSettings->mHost);
+ if (((results = strtok(copy, "%")) != NULL) && ((results = strtok(NULL, "%")) != NULL)) {
+ inSettings->mIfrname = calloc (strlen(results) + 1, sizeof(char));
+ strcpy(inSettings->mIfrname, results);
+ }
+ free(copy);
+ } else if ((inSettings->mThreadMode == kMode_Server) && (IN6_IS_ADDR_V4MAPPED (&addr->sin6_addr))) {
+ for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
+ if ((ifa->ifa_addr) && (ifa->ifa_addr->sa_family == AF_INET)) {
+ struct sockaddr_in* inaddr = (struct sockaddr_in*)ifa->ifa_addr;
+ uint32_t v4;
+ memcpy(&v4, &addr->sin6_addr.s6_addr[12], 4);
+ if ((ifa->ifa_name) && (inaddr->sin_addr.s_addr == v4)) {
+ // Found v4 address in v4 addr family, copy it to thread settings structure
+ inSettings->mIfrname = calloc (strlen(ifa->ifa_name) + 1, sizeof(char));
+ strcpy(inSettings->mIfrname, ifa->ifa_name);
+ break;
+ }
+ }
+ }
+ } else {
+ // Hunt the v6 interfaces
+ for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
+ if ((ifa->ifa_addr) && (ifa->ifa_addr->sa_family == AF_INET6)) {
+ struct sockaddr_in6* inaddr = (struct sockaddr_in6*)ifa->ifa_addr;
+ if ((ifa->ifa_name) && (IN6_ARE_ADDR_EQUAL(&addr->sin6_addr, &inaddr->sin6_addr))) {
+ // Found v6 address in v6 addr family, copy it to thread settings structure
+ inSettings->mIfrname = calloc (strlen(ifa->ifa_name) + 1, sizeof(char));
+ strcpy(inSettings->mIfrname, ifa->ifa_name);
+ break;
+ }
+ }
+ }
+ }
+ }
+ freeifaddrs(ifaddr);
+ }
+#endif
+ return ((inSettings->mIfrname == NULL) ? -1 : 0);
+}
+
+
+#if defined(HAVE_LINUX_FILTER_H) && defined(HAVE_AF_PACKET)
+int SockAddr_Drop_All_BPF (int sock) {
+ struct sock_filter udp_filter[] = {
+ { 0x6, 0, 0, 0x00000000 },
+ };
+ struct sock_fprog bpf = {
+ .len = (sizeof(udp_filter) / sizeof(struct sock_filter)),
+ .filter = udp_filter,
+ };
+ return(setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER, &bpf, sizeof(bpf)));
+}
+
+//
+// [root@ryzen3950 iperf2-code]# tcpdump ip and udp dst port 5001 -e -dd -i any
+// tcpdump: data link type LINUX_SLL2
+// { 0x28, 0, 0, 0x00000000 },
+// { 0x15, 0, 8, 0x00000800 },
+// { 0x30, 0, 0, 0x0000001d },
+// { 0x15, 0, 6, 0x00000011 },
+// { 0x28, 0, 0, 0x0000001a },
+// { 0x45, 4, 0, 0x00001fff },
+// { 0xb1, 0, 0, 0x00000014 },
+// { 0x48, 0, 0, 0x00000016 },
+// { 0x15, 0, 1, 0x00001389 },
+// { 0x6, 0, 0, 0x00040000 },
+// { 0x6, 0, 0, 0x00000000 },
+
+// { 0x28, 0, 0, 0x00000014 },
+// { 0x45, 4, 0, 0x00001fff },
+// { 0xb1, 0, 0, 0x0000001e },
+// { 0x48, 0, 0, 0x00000010 },
+// { 0x15, 0, 1, 0x00001389 },
+
+
+int SockAddr_v4_Accept_BPF (int sock, uint16_t port) {
+ // tcpdump udp dst port 5001 -dd to get c code filter
+ // UDP port is the 5 and 13 bytecodes (5001 = 0x1389)
+ // see linux/filter.h
+ struct sock_filter udp_filter[] = {
+ { 0x28, 0, 0, 0x0000000c },
+ { 0x15, 0, 8, 0x00000800 },
+ { 0x30, 0, 0, 0x00000017 },
+ { 0x15, 0, 6, 0x00000011 },
+ { 0x28, 0, 0, 0x00000014 },
+ { 0x45, 4, 0, 0x00001fff },
+ { 0xb1, 0, 0, 0x0000000e },
+ { 0x48, 0, 0, 0x00000010 },
+ { 0x15, 0, 1, 0x00001389 },
+ { 0x6, 0, 0, 0x00040000 },
+ { 0x6, 0, 0, 0x00000000 },
+ };
+ udp_filter[8].k = port;
+ struct sock_fprog bpf = {
+ .len = (sizeof(udp_filter) / sizeof(struct sock_filter)),
+ .filter = udp_filter,
+ };
+ return(setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER, &bpf, sizeof(bpf)));
+}
+
+//[root@ryzen3950 iperf2-code]# tcpdump udp dst port 5001 and dst host 1.1.1.1 -dd
+//Warning: assuming Ethernet
+//{ 0x28, 0, 0, 0x0000000c },
+//{ 0x15, 11, 0, 0x000086dd },
+//{ 0x15, 0, 10, 0x00000800 },
+//{ 0x30, 0, 0, 0x00000017 },
+//{ 0x15, 0, 8, 0x00000011 },
+//{ 0x28, 0, 0, 0x00000014 },
+//{ 0x45, 6, 0, 0x00001fff },
+//{ 0xb1, 0, 0, 0x0000000e },
+//{ 0x48, 0, 0, 0x00000010 },
+//{ 0x15, 0, 3, 0x00001389 },
+//{ 0x20, 0, 0, 0x0000001e },
+//{ 0x15, 0, 1, 0x01010101 },
+//{ 0x6, 0, 0, 0x00040000 },
+//{ 0x6, 0, 0, 0x00000000 },
+//
+// BPF for TAP interaces with explicit v4 IP and UDP dst port
+int SockAddr_Accept_V4_TAP_BPF (int sock, uint32_t dstip, uint16_t port) {
+ struct sock_filter udp_filter[] = {
+ { 0x28, 0, 0, 0x0000000c },
+ { 0x15, 11, 0, 0x000086dd },
+ { 0x15, 0, 10, 0x00000800 },
+ { 0x30, 0, 0, 0x00000017 },
+ { 0x15, 0, 8, 0x00000011 },
+ { 0x28, 0, 0, 0x00000014 },
+ { 0x45, 6, 0, 0x00001fff },
+ { 0xb1, 0, 0, 0x0000000e },
+ { 0x48, 0, 0, 0x00000010 },
+ { 0x15, 0, 3, 0x00001389 },
+ { 0x20, 0, 0, 0x0000001e },
+ { 0x15, 0, 1, 0x00000000 },
+ { 0x6, 0, 0, 0x00040000 },
+ { 0x6, 0, 0, 0x00000000 },
+ };
+ udp_filter[12].k = htonl(dstip);
+ udp_filter[9].k = htons(port);
+ struct sock_fprog bpf = {
+ .len = (sizeof(udp_filter) / sizeof(struct sock_filter)),
+ .filter = udp_filter,
+ };
+ return(setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER, &bpf, sizeof(bpf)));
+}
+
+// Simulate the UDP connect for the AF_PACKET (or PF_PACKET)
+//
+int SockAddr_v4_Connect_BPF (int sock, uint32_t dstip, uint32_t srcip, uint16_t dstport, uint16_t srcport) {
+ // Use full quintuple, proto, src ip, dst ip, src port, dst port
+ // ip proto is already set per the PF_PACKET ETH_P_IP
+ // tcpdump udp and ip src 127.0.0.1 and ip dst 127.0.0.2 and src port 5001 and dst port 5002 -dd
+ //
+ // tcpdump udp and ip src 127.0.0.1 and ip dst 127.0.0.2 and src port 5001 and dst port 5002 -d
+ // (000) ldh [12]
+ // (001) jeq #0x86dd jt 17 jf 2
+ // (002) jeq #0x800 jt 3 jf 17
+ // (003) ldb [23]
+ // (004) jeq #0x11 jt 5 jf 17
+ // (005) ld [26]
+ // (006) jeq #0x7f000001 jt 7 jf 17
+ // (007) ld [30]
+ // (008) jeq #0x7f000002 jt 9 jf 17
+ // (009) ldh [20]
+ // (010) jset #0x1fff jt 17 jf 11
+ // (011) ldxb 4*([14]&0xf)
+ // (012) ldh [x + 14]
+ // (013) jeq #0x1389 jt 14 jf 17
+ // (014) ldh [x + 16]
+ // (015) jeq #0x138a jt 16 jf 17
+ // (016) ret #262144
+ // (017) ret #0
+ //
+ struct sock_filter udp_filter[] = {
+ { 0x28, 0, 0, 0x0000000c },
+ { 0x15, 15, 0, 0x000086dd },
+ { 0x15, 0, 14, 0x00000800 },
+ { 0x30, 0, 0, 0x00000017 },
+ { 0x15, 0, 12, 0x00000011 },
+ { 0x20, 0, 0, 0x0000001a },
+ { 0x15, 0, 10, 0x7f000001 },
+ { 0x20, 0, 0, 0x0000001e },
+ { 0x15, 0, 8, 0x7f000002 },
+ { 0x28, 0, 0, 0x00000014 },
+ { 0x45, 6, 0, 0x00001fff },
+ { 0xb1, 0, 0, 0x0000000e },
+ { 0x48, 0, 0, 0x0000000e },
+ { 0x15, 0, 3, 0x00001389 },
+ { 0x48, 0, 0, 0x00000010 },
+ { 0x15, 0, 1, 0x0000138a },
+ { 0x6, 0, 0, 0x00040000 },
+ { 0x6, 0, 0, 0x00000000 },
+ };
+ udp_filter[6].k = htonl(srcip);
+ udp_filter[8].k = htonl(dstip);
+ udp_filter[13].k = htons(srcport);
+ udp_filter[15].k = htons(dstport);
+ struct sock_fprog bpf = {
+ .len = (sizeof(udp_filter) / sizeof(struct sock_filter)),
+ .filter = udp_filter,
+ };
+ return(setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER, &bpf, sizeof(bpf)));
+}
+int SockAddr_v4_Connect_TAP_BPF (int sock, uint32_t dstip, uint32_t srcip, uint16_t dstport, uint16_t srcport) {
+ // [root@ryzen3950 iperf2-code]# tcpdump ip and src 127.0.0.1 and dst 127.0.0.2 and udp src port 5001 and dst port 5002 -d
+ // Warning: assuming Ethernet
+ // (000) ldh [12]
+ // (001) jeq #0x800 jt 2 jf 16
+ // (002) ld [26]
+ // (003) jeq #0x7f000001 jt 4 jf 16
+ // (004) ld [30]
+ // (005) jeq #0x7f000002 jt 6 jf 16
+ // (006) ldb [23]
+ // (007) jeq #0x11 jt 8 jf 16
+ // (008) ldh [20]
+ // (009) jset #0x1fff jt 16 jf 10
+ // (010) ldxb 4*([14]&0xf)
+ // (011) ldh [x + 14]
+ // (012) jeq #0x1389 jt 13 jf 16
+ // (013) ldh [x + 16]
+ // (014) jeq #0x138a jt 15 jf 16
+ // (015) ret #262144
+ // (016) ret #0
+ // [root@ryzen3950 iperf2-code]# tcpdump ip and src 127.0.0.1 and dst 127.0.0.2 and udp src port 5001 and dst port 5002 -dd
+ // Warning: assuming Ethernet
+ struct sock_filter udp_filter[] = {
+ { 0x28, 0, 0, 0x0000000c },
+ { 0x15, 0, 14, 0x00000800 },
+ { 0x20, 0, 0, 0x0000001a },
+ { 0x15, 0, 12, 0x7f000001 },
+ { 0x20, 0, 0, 0x0000001e },
+ { 0x15, 0, 10, 0x7f000002 },
+ { 0x30, 0, 0, 0x00000017 },
+ { 0x15, 0, 8, 0x00000011 },
+ { 0x28, 0, 0, 0x00000014 },
+ { 0x45, 6, 0, 0x00001fff },
+ { 0xb1, 0, 0, 0x0000000e },
+ { 0x48, 0, 0, 0x0000000e },
+ { 0x15, 0, 3, 0x00001389 },
+ { 0x48, 0, 0, 0x00000010 },
+ { 0x15, 0, 1, 0x0000138a },
+ { 0x6, 0, 0, 0x00040000 },
+ { 0x6, 0, 0, 0x00000000 },
+ };
+ udp_filter[3].k = htonl(srcip);
+ udp_filter[5].k = htonl(dstip);
+ udp_filter[12].k = htons(srcport);
+ udp_filter[14].k = htons(dstport);
+ struct sock_fprog bpf = {
+ .len = (sizeof(udp_filter) / sizeof(struct sock_filter)),
+ .filter = udp_filter,
+ };
+ return(setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER, &bpf, sizeof(bpf)));
+}
+
+int SockAddr_v4_Connect_BPF_Drop (int sock, uint32_t dstip, uint32_t srcip, uint16_t dstport, uint16_t srcport) {
+ // Use full quintuple, proto, src ip, dst ip, src port, dst port
+ // ip proto is already set per the PF_PACKET ETH_P_IP
+ // tcpdump udp and ip src 127.0.0.1 and ip dst 127.0.0.2 and src port 5001 and dst port 5002 -dd
+
+ struct sock_filter udp_filter[] = {
+ { 0x28, 0, 0, 0x0000000c },
+ { 0x15, 15, 0, 0x000086dd },
+ { 0x15, 0, 14, 0x00000800 },
+ { 0x30, 0, 0, 0x00000017 },
+ { 0x15, 0, 12, 0x00000011 },
+ { 0x20, 0, 0, 0x0000001a },
+ { 0x15, 0, 10, 0x7f000001 },
+ { 0x20, 0, 0, 0x0000001e },
+ { 0x15, 0, 8, 0x7f000002 },
+ { 0x28, 0, 0, 0x00000014 },
+ { 0x45, 6, 0, 0x00001fff },
+ { 0xb1, 0, 0, 0x0000000e },
+ { 0x48, 0, 0, 0x0000000e },
+ { 0x15, 0, 3, 0x00001389 },
+ { 0x48, 0, 0, 0x00000010 },
+ { 0x15, 0, 1, 0x0000138a },
+ { 0x6, 0, 0, 0x00000000 },
+ { 0x6, 0, 0, 0x00000000 },
+ };
+ udp_filter[6].k = htonl(srcip);
+ udp_filter[8].k = htonl(dstip);
+ udp_filter[13].k = htons(srcport);
+ udp_filter[15].k = htons(dstport);
+ struct sock_fprog bpf = {
+ .len = (sizeof(udp_filter) / sizeof(struct sock_filter)),
+ .filter = udp_filter,
+ };
+ return(setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER, &bpf, sizeof(bpf)));
+}
+#if HAVE_IPV6
+//
+// v6 Connected BPF, use 32 bit values
+//
+int SockAddr_v6_Connect_BPF (int sock, struct in6_addr *dst, struct in6_addr *src, uint16_t dstport, uint16_t srcport) {
+ // Use full quintuple, proto, src ip, dst ip, src port, dst port
+ // tcpdump udp and ip6 src fe80::428d:5cff:fe6a:2d85 and ip6 dst fe80::428d:5cff:fe6a:2d86 and src port 5001 and dst port 5002 -dd
+ //
+ //tcpdump udp and ip6 src fe80::428d:5cff:fe6a:2d85 and ip6 dst fe80::428d:5cff:fe6a:2d86 and src port 5001 and dst port 5002 -d
+ // (000) ldh [12]
+ // (001) jeq #0x86dd jt 2 jf 32
+ // (002) ldb [20]
+ // (003) jeq #0x11 jt 7 jf 4
+ // (004) jeq #0x2c jt 5 jf 32
+ // (005) ldb [54]
+ // (006) jeq #0x11 jt 7 jf 32
+ // (007) ld [22]
+ // (008) jeq #0xfe800000 jt 9 jf 32
+ // (009) ld [26]
+ // (010) jeq #0x0 jt 11 jf 32
+ // (011) ld [30]
+ // (012) jeq #0x428d5cff jt 13 jf 32
+ // (013) ld [34]
+ // (014) jeq #0xfe6a2d85 jt 15 jf 32
+ // (015) ld [38]
+ // (016) jeq #0xfe800000 jt 17 jf 32
+ // (017) ld [42]
+ // (018) jeq #0x0 jt 19 jf 32
+ // (019) ld [46]
+ // (020) jeq #0x428d5cff jt 21 jf 32
+ // (021) ld [50]
+ // (022) jeq #0xfe6a2d86 jt 23 jf 32
+ // (023) ldb [20]
+ // (024) jeq #0x84 jt 27 jf 25
+ // (025) jeq #0x6 jt 27 jf 26
+ // (026) jeq #0x11 jt 27 jf 32
+ // (027) ldh [54]
+ // (028) jeq #0x1389 jt 29 jf 32
+ // (029) ldh [56]
+ // (030) jeq #0x138a jt 31 jf 32
+ // (031) ret #262144
+ // (032) ret #0
+ //
+ struct sock_filter udp_filter[] = {
+ { 0x28, 0, 0, 0x0000000c },
+ { 0x15, 0, 30, 0x000086dd },
+ { 0x30, 0, 0, 0x00000014 },
+ { 0x15, 3, 0, 0x00000011 },
+ { 0x15, 0, 27, 0x0000002c },
+ { 0x30, 0, 0, 0x00000036 },
+ { 0x15, 0, 25, 0x00000011 },
+ { 0x20, 0, 0, 0x00000016 },
+ { 0x15, 0, 23, 0xfe800000 },
+ { 0x20, 0, 0, 0x0000001a },
+ { 0x15, 0, 21, 0x00000000 },
+ { 0x20, 0, 0, 0x0000001e },
+ { 0x15, 0, 19, 0x428d5cff },
+ { 0x20, 0, 0, 0x00000022 },
+ { 0x15, 0, 17, 0xfe6a2d85 },
+ { 0x20, 0, 0, 0x00000026 },
+ { 0x15, 0, 15, 0xfe800000 },
+ { 0x20, 0, 0, 0x0000002a },
+ { 0x15, 0, 13, 0x00000000 },
+ { 0x20, 0, 0, 0x0000002e },
+ { 0x15, 0, 11, 0x428d5cff },
+ { 0x20, 0, 0, 0x00000032 },
+ { 0x15, 0, 9, 0xfe6a2d86 },
+ { 0x30, 0, 0, 0x00000014 },
+ { 0x15, 2, 0, 0x00000084 },
+ { 0x15, 1, 0, 0x00000006 },
+ { 0x15, 0, 5, 0x00000011 },
+ { 0x28, 0, 0, 0x00000036 },
+ { 0x15, 0, 3, 0x00001389 },
+ { 0x28, 0, 0, 0x00000038 },
+ { 0x15, 0, 1, 0x0000138a },
+ { 0x6, 0, 0, 0x00040000 },
+ { 0x6, 0, 0, 0x00000000 },
+ };
+ udp_filter[8].k = htonl((*src).s6_addr32[0]);
+ udp_filter[10].k = htonl((*src).s6_addr32[1]);
+ udp_filter[12].k = htonl((*src).s6_addr32[2]);
+ udp_filter[14].k = htonl((*src).s6_addr32[3]);
+ udp_filter[16].k = htonl((*dst).s6_addr32[0]);
+ udp_filter[18].k = htonl((*dst).s6_addr32[1]);
+ udp_filter[20].k = htonl((*dst).s6_addr32[2]);
+ udp_filter[22].k = htonl((*dst).s6_addr32[3]);
+ udp_filter[28].k = htons(srcport);
+ udp_filter[30].k = htons(dstport);
+ struct sock_fprog bpf = {
+ .len = (sizeof(udp_filter) / sizeof(struct sock_filter)),
+ .filter = udp_filter,
+ };
+ return(setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER, &bpf, sizeof(bpf)));
+}
+# endif // HAVE_V6
+#endif // HAVE_LINUX_FILTER
+
+#ifdef __cplusplus
+} /* end extern "C" */
+#endif
diff --git a/src/active_hosts.cpp b/src/active_hosts.cpp
new file mode 100644
index 0000000..a3455ab
--- /dev/null
+++ b/src/active_hosts.cpp
@@ -0,0 +1,261 @@
+/*---------------------------------------------------------------
+ * Copyrig h(c) 1999,2000,2001,2002,2003
+ * The Board of Trustees of the University of Illinois
+ * All Rights Reserved.
+ *---------------------------------------------------------------
+ * 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.
+ * ________________________________________________________________
+ * National Laboratory for Applied Network Research
+ * National Center for Supercomputing Applications
+ * University of Illinois at Urbana-Champaign
+ * http://www.ncsa.uiuc.edu
+ * ________________________________________________________________
+ *
+ * active_hosts.c (was List.cpp)
+ * rewrite by Robert McMahon
+ *
+ * This is a list to hold active traffic and create sum groups
+ * sum groups are traffic sessions from the same client host
+ * -------------------------------------------------------------------
+ */
+
+#include "active_hosts.h"
+#include "Mutex.h"
+#include "SocketAddr.h"
+#include "Reporter.h"
+
+/*
+ * Global table with active hosts, their sum reports and active thread counts
+ */
+static struct Iperf_Table active_table;
+static bool Iperf_host_port_present (iperf_sockaddr *find);
+static struct Iperf_ListEntry* Iperf_host_present (iperf_sockaddr *find);
+
+#if HAVE_THREAD_DEBUG
+static void active_table_show_entry(const char *action, Iperf_ListEntry *entry, int found) {
+ assert(action != NULL);
+ assert(entry != NULL);
+ char tmpaddr[200];
+ size_t len=200;
+ unsigned short port = SockAddr_getPort(&(entry->host));
+ SockAddr_getHostAddress(&(entry->host), tmpaddr, len);
+ thread_debug("active table: %s %s port %d (flag=%d) rootp=%p entryp=%p totcnt/activecnt/hostcnt = %d/%d/%d", \
+ action, tmpaddr, port, found, (void *) active_table.root, (void *) entry, active_table.total_count, \
+ active_table.count, entry->thread_count);
+}
+static void active_table_show_compare(const char *action, Iperf_ListEntry *entry, iperf_sockaddr *host, const char *type) {
+ assert(action != NULL);
+ assert(entry != NULL);
+ char lookupaddr[200];
+ char findaddr[200];
+ size_t len=200;
+ unsigned short port = SockAddr_getPort(&(entry->host));
+ unsigned short findport = SockAddr_getPort(host);
+ SockAddr_getHostAddress(&(entry->host), lookupaddr, len);
+ SockAddr_getHostAddress(host, findaddr, len);
+ thread_debug("active table: compare table entry %s %s/%d against host %s/%d (%s)", type, lookupaddr, port, findaddr, findport, action);
+}
+#endif
+
+void Iperf_initialize_active_table () {
+ Mutex_Initialize(&active_table.my_mutex);
+ active_table.root = NULL;
+ active_table.groupid = 0;
+}
+
+/*
+ * Add Entry add to the list or update thread count
+ */
+static void active_table_update (iperf_sockaddr *host, struct thread_Settings *agent) {
+ assert(host != NULL);
+ assert(agent != NULL);
+ Iperf_ListEntry *this_entry = Iperf_host_present(host);
+ active_table.total_count++;
+ if (this_entry == NULL) {
+ this_entry = new Iperf_ListEntry();
+ assert(this_entry != NULL);
+ this_entry->host = *host;
+ this_entry->next = active_table.root;
+ this_entry->thread_count = 1;
+ this_entry->socket = agent->mSock;
+ active_table.count++;
+ active_table.groupid++;
+ active_table.root = this_entry;
+ this_entry->sum_report = InitSumReport(agent, active_table.total_count, 0);
+ IncrSumReportRefCounter(this_entry->sum_report);
+ agent->mSumReport = this_entry->sum_report;
+ this_entry->sum_report->info.common->transferID = -active_table.groupid; // sum ids are negative
+#if HAVE_THREAD_DEBUG
+ active_table_show_entry("new entry", this_entry, ((SockAddr_are_Equal(&this_entry->host, host) && SockAddr_Hostare_Equal(&this_entry->host, host))));
+#endif
+ } else {
+ this_entry->thread_count++;
+ agent->mSumReport = this_entry->sum_report;
+ IncrSumReportRefCounter(this_entry->sum_report);
+#if HAVE_THREAD_DEBUG
+ active_table_show_entry("incr entry", this_entry, 1);
+#endif
+ }
+}
+
+static inline iperf_sockaddr *active_table_get_host_key (struct thread_Settings *agent) {
+ iperf_sockaddr *key = (isSumServerDstIP(agent) ? &agent->local : &agent->peer);
+ return key;
+}
+
+// Thread access to store a host
+int Iperf_push_host (struct thread_Settings *agent) {
+ iperf_sockaddr *host = active_table_get_host_key(agent);
+ Mutex_Lock(&active_table.my_mutex);
+ active_table_update(host, agent);
+ int groupid = active_table.groupid;
+ Mutex_Unlock(&active_table.my_mutex);
+ return groupid;
+}
+
+// Used for UDP push of a new host, returns negative value if the host/port is already present
+// This is critical because UDP is connectionless and designed to be stateless
+int Iperf_push_host_port_conditional (struct thread_Settings *agent) {
+ iperf_sockaddr *host = active_table_get_host_key(agent);
+ int rc = -1;
+ Mutex_Lock(&active_table.my_mutex);
+ if (!Iperf_host_port_present(host)) {
+ active_table_update(host, agent);
+ rc = active_table.groupid;
+ }
+ Mutex_Unlock(&active_table.my_mutex);
+ return (rc);
+}
+
+/*
+ * Remove a host from the table
+ */
+void Iperf_remove_host (struct thread_Settings *agent) {
+ iperf_sockaddr *del = active_table_get_host_key(agent);
+ // remove_list_entry(entry) {
+ // indirect = &head;
+ // while ((*indirect) != entry) {
+ // indirect = &(*indirect)->next;
+ // }
+ // *indirect = entry->next
+ Mutex_Lock(&active_table.my_mutex);
+ Iperf_ListEntry **tmp = &active_table.root;
+ while ((*tmp) && !(SockAddr_Hostare_Equal(&(*tmp)->host, del))) {
+#if HAVE_THREAD_DEBUG
+ active_table_show_compare("miss", *tmp, del, "client ip");
+#endif
+ tmp = &(*tmp)->next;
+ }
+ if (*tmp) {
+ if (--(*tmp)->thread_count == 0) {
+ Iperf_ListEntry *remove = (*tmp);
+ active_table.count--;
+ agent->mSumReport = NULL;
+#if HAVE_THREAD_DEBUG
+ active_table_show_entry("delete", remove, 1);
+#endif
+ *tmp = remove->next;
+ FreeSumReport(remove->sum_report);
+ delete remove;
+ } else {
+ DecrSumReportRefCounter((*tmp)->sum_report);
+#if HAVE_THREAD_DEBUG
+ active_table_show_entry("decr", (*tmp), 1);
+#endif
+ }
+ }
+ Mutex_Unlock(&active_table.my_mutex);
+}
+
+/*
+ * Destroy the table
+ */
+void Iperf_destroy_active_table () {
+ Iperf_ListEntry *itr1 = active_table.root, *itr2;
+ while (itr1 != NULL) {
+ itr2 = itr1->next;
+ delete itr1;
+ itr1 = itr2;
+ }
+ Mutex_Destroy(&active_table.my_mutex);
+ active_table.root = NULL;
+ active_table.count = 0;
+ active_table.total_count = 0;
+}
+
+/*
+ * Check if the host and port are present in the active table
+ */
+bool Iperf_host_port_present (iperf_sockaddr *find) {
+ Iperf_ListEntry *itr = active_table.root;
+ bool rc = false;
+ while (itr != NULL) {
+ if (SockAddr_are_Equal(&itr->host, find)) {
+#if HAVE_THREAD_DEBUG
+ active_table_show_compare("match", itr, find, "client ip/port");
+#endif
+ rc = true;
+ break;
+ } else {
+#if HAVE_THREAD_DEBUG
+ active_table_show_compare("miss", itr, find, "client ip/port");
+#endif
+ itr = itr->next;
+ }
+ }
+ return rc;
+}
+
+/*
+ * Check if the host is present in the active table
+ */
+static Iperf_ListEntry* Iperf_host_present (iperf_sockaddr *find) {
+ Iperf_ListEntry *itr = active_table.root;
+ while (itr != NULL) {
+ if (SockAddr_Hostare_Equal(&itr->host, find)) {
+#if HAVE_THREAD_DEBUG
+ active_table_show_compare("match", itr, find, "client ip");
+#endif
+ break;
+ } else {
+#if HAVE_THREAD_DEBUG
+ active_table_show_compare("miss", itr, find, "client ip");
+#endif
+ itr = itr->next;
+ }
+ }
+ return itr;
+}
diff --git a/src/checkdelay.c b/src/checkdelay.c
new file mode 100644
index 0000000..1c35d64
--- /dev/null
+++ b/src/checkdelay.c
@@ -0,0 +1,203 @@
+/*---------------------------------------------------------------
+ * Copyright (c) 2014
+ * Broadcom Corporation
+ * All Rights Reserved.
+ *---------------------------------------------------------------
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software 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 name of Broadcom Coporation,
+ * 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.
+ * ________________________________________________________________
+ *
+ * checkdelay.c
+ * Simple tool to measure mean/min/max of nanosleep
+ * by Robert J. McMahon (rjmcmahon@rjmcmahon.com, rmcmahon@broadcom.com)
+ * ------------------------------------------------------------------- */
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <ctype.h>
+#include <unistd.h>
+#include "headers.h"
+#include "util.h"
+#include "delay.h"
+#if HAVE_SCHED_SETSCHEDULER
+#include <sched.h>
+#ifdef HAVE_MLOCKALL
+#include <sys/mman.h>
+#endif
+#endif
+
+
+
+#define BILLION 1000000000
+#define MILLION 1000000
+
+int main (int argc, char **argv) {
+ double sum=0;
+ double time1, time2;
+ double delta, max=0, min=-1;
+ int ix, delay=1,loopcount=1000;
+ int c;
+ int clockgettime = 0, kalman = 0;
+#if HAVE_DECL_CPU_SET
+ int affinity = 0;
+#endif
+#if HAVE_SCHED_SETSCHEDULER
+ int realtime = 0;
+ struct sched_param sp;
+#endif
+#ifdef HAVE_CLOCK_GETTIME
+ struct timespec t1;
+#else
+ struct timeval t1;
+#endif
+
+ while ((c=getopt(argc, argv, "a:bkd:i:r")) != -1)
+ switch (c) {
+ case 'b':
+ clockgettime = 1;
+ break;
+ case 'k':
+ kalman = 1;
+ break;
+ case 'd':
+ delay = atoi(optarg);
+ break;
+ case 'i':
+ loopcount = atoi(optarg);
+ break;
+#if HAVE_DECL_CPU_SET
+ case 'a':
+ affinity=atoi(optarg);
+ break;
+#endif
+#if HAVE_SCHED_SETSCHEDULER
+ case 'r':
+ realtime = 1;
+ break;
+#endif
+ case '?':
+ fprintf(stderr,"Usage -b busyloop, -d usec delay, -i iterations"
+#if HAVE_DECL_CPU_SET
+ ", -a affinity"
+#endif
+#if HAVE_SCHED_SETSCHEDULER
+ ", -r realtime"
+#endif
+ "\n");
+ return 1;
+ default:
+ abort();
+ }
+
+#ifndef HAVE_NANOSLEEP
+ clockgettime = 1;
+#endif
+
+#if HAVE_SCHED_SETSCHEDULER
+ if (realtime) {
+ fprintf(stdout,"Setting scheduler to realtime via SCHED_RR\n");
+ // SCHED_OTHER, SCHED_FIFO, SCHED_RR
+ sp.sched_priority = sched_get_priority_max(SCHED_RR);
+ WARN_errno(sched_setscheduler(0, SCHED_RR, &sp) < 0,
+ "Client set scheduler");
+#ifdef HAVE_MLOCKALL
+ // lock the threads memory
+ WARN_errno(mlockall(MCL_CURRENT | MCL_FUTURE) != 0, "mlockall");
+#endif
+ }
+#if HAVE_DECL_CPU_SET
+ if (affinity) {
+ fprintf(stdout,"CPU affinity set to %d\n", affinity);
+ cpu_set_t myset;
+ CPU_ZERO(&myset);
+ CPU_SET(affinity,&myset);
+ }
+#endif
+#endif
+ if (loopcount > 1000)
+ fprintf(stdout,"Measuring %s over %.0e iterations using %d usec delay\n",
+ kalman ? "kalman" :
+ clockgettime ? "clock_gettime" : "nanosleep",
+ (double) loopcount, delay);
+ else
+ fprintf(stdout,"Measuring %s over %d iterations using %d usec delay\n",
+ kalman ? "kalman" :
+ clockgettime ? "clock_gettime" : "nanosleep",
+ loopcount, delay);
+ fflush(stdout);
+ for (ix=0; ix < loopcount; ix++) {
+ // Find the max jitter for delay call
+#ifdef HAVE_CLOCK_GETTIME
+ clock_gettime(CLOCK_REALTIME, &t1);
+ time1 = t1.tv_sec + (t1.tv_nsec / 1000000000.0);
+#else
+ gettimeofday( &t1, NULL );
+ time1 = t1.tv_sec + (t1.tv_usec / 1000000.0);
+#endif
+#ifdef HAVE_KALMAN
+ if (kalman) {
+ delay_kalman(delay);
+ } else
+#endif
+ if (clockgettime) {
+ delay_busyloop(delay);
+ } else {
+#ifdef HAVE_NANOSLEEP
+ delay_nanosleep(delay);
+#endif
+ }
+#ifdef HAVE_CLOCK_GETTIME
+ clock_gettime(CLOCK_REALTIME, &t1);
+ time2 = t1.tv_sec + (t1.tv_nsec / 1000000000.0);
+#else
+ gettimeofday( &t1, NULL );
+ time2 = t1.tv_sec + (t1.tv_usec / 1000000.0);
+#endif
+ delta = (time2 - time1) * 1e6;
+ if (delta > max) {
+ max = delta;
+ }
+ if (delta < min || min < 0) {
+ min = delta;
+ }
+ sum += (double) delta;
+ }
+ fprintf(stdout,"delay=%.03f/%.03f/%.03f usec (mean/min/max)\n",
+ (sum / loopcount), min, max);
+ return(0);
+}
diff --git a/src/checkisoch.cpp b/src/checkisoch.cpp
new file mode 100644
index 0000000..a770e6d
--- /dev/null
+++ b/src/checkisoch.cpp
@@ -0,0 +1,129 @@
+/*---------------------------------------------------------------
+ * Copyright (c) 2017
+ * Broadcom Corporation
+ * All Rights Reserved.
+ *---------------------------------------------------------------
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software 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 name of Broadcom Coporation,
+ * 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.
+ * ________________________________________________________________
+ *
+ * checkisoch.cpp
+ *
+ * Test routine to test the isochronous functions
+ *
+ * by Robert J. McMahon (rjmcmahon@rjmcmahon.com, bob.mcmahon@broadcom.com)
+ * ------------------------------------------------------------------- */
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <errno.h>
+#include <string.h>
+#include <math.h>
+#include <unistd.h>
+#include "headers.h"
+#include "isochronous.hpp"
+#include "delay.h"
+#include "pdfs.h"
+#include "util.h"
+
+static void posttimestamp(int, int);
+
+int main (int argc, char **argv) {
+ int c, count=100, frequency=100;
+ float mean=1e8;
+ float variance=3e7;
+ bool forceslip = false;
+
+ Isochronous::FrameCounter *fc = NULL;
+
+ while ((c = getopt(argc, argv, "c:f:m:sv:")) != -1) {
+ switch (c) {
+ case 'c':
+ count = atoi(optarg);
+ break;
+ case 'f':
+ frequency = atoi(optarg);
+ break;
+ case 'm':
+ mean=byte_atof(optarg);
+ break;
+ case 's':
+ forceslip = true;
+ break;
+ case 'v':
+ variance=byte_atof(optarg);
+ break;
+ case '?':
+ fprintf (stderr, "usage: -c <count> -f <frames per second> -m <mean> -v <variance>\n");
+ return 1;
+ default:
+ abort ();
+ }
+ }
+ fc = new Isochronous::FrameCounter(frequency);
+
+ fprintf(stdout,"Timestamping %d times at %d fps\n", count, frequency);
+ fflush(stdout);
+ while (count-- > 0) {
+ if (forceslip && count == 8) {
+ delay_loop (1000000/frequency + 10);
+ }
+ fc->wait_tick();
+ posttimestamp(count, (round(lognormal(mean,variance)) / (frequency * 8)));
+ if (fc->slip) {
+ fprintf(stdout,"Slip occurred\n");
+ fc->slip = 0;
+ }
+ }
+ DELETE_PTR(fc);
+}
+
+void posttimestamp (int count, int bytes) {
+ struct timespec t1;
+ double timestamp;
+ int err;
+
+ err = clock_gettime(CLOCK_REALTIME, &t1);
+ if (err < 0) {
+ perror("clock_getttime");
+ } else {
+ timestamp = t1.tv_sec + (t1.tv_nsec / 1000000000.0);
+ fprintf(stdout,"%f counter(%d), sending %d bytes\n", timestamp, count, bytes);
+ }
+ fflush(stdout);
+}
diff --git a/src/checkpdfs.c b/src/checkpdfs.c
new file mode 100644
index 0000000..87577c7
--- /dev/null
+++ b/src/checkpdfs.c
@@ -0,0 +1,217 @@
+/*---------------------------------------------------------------
+ * Copyright (c) 2017
+ * Broadcom Corporation
+ * All Rights Reserved.
+ *---------------------------------------------------------------
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software 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 name of Broadcom Coporation,
+ * 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.
+ * ________________________________________________________________
+ *
+ * pdfs.c
+ * Produce probability distribution functions, expected to be used
+ * for iperf client traffic emulations
+ *
+ * by Robert J. McMahon (rjmcmahon@rjmcmahon.com, bob.mcmahon@broadcom.com)
+ * ------------------------------------------------------------------- */
+
+/* Produce normal and log normal
+ *
+ * Implements the Polar form of the Box-Muller Transformation
+ *
+ *
+*/
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <math.h>
+#include <unistd.h>
+#include "headers.h"
+#include "util.h"
+#include "pdfs.h"
+
+#define MAXBINS 1024
+#define TRUE 1
+#define FALSE 0
+#define MILLION 1000000
+#define BILLION 1000000000
+
+#ifdef HAVE_CLOCK_GETTIME
+static double timespec_diff(struct timespec tv1, struct timespec tv0);
+#else
+static double timeval_diff (struct timeval tv1, struct timeval tv0);
+#endif
+
+int main (int argc, char **argv) {
+ int c, i;
+ int minbin = MAXBINS;
+ int maxbin = 0;
+ int bincount=MAXBINS;
+ time_t t;
+ int histogram[MAXBINS];
+ float mean=100.0;
+ float variance=30.0;
+ int count = 10000;
+ int gaussian = TRUE;
+ int printout = FALSE;
+ int speedonly = FALSE;
+ int exectime;
+ int random = FALSE;
+ double total;
+ double binwidth=1.0;
+
+ while ((c=getopt(argc, argv, "b:c:lm:prsv:w:")) != -1)
+ switch (c) {
+ case 'b':
+ bincount = atoi(optarg);
+ break;
+ case 'c':
+ count = atoi(optarg);
+ break;
+ case 'l':
+ gaussian = FALSE;
+ break;
+ case 'm':
+ mean = bitorbyte_atof(optarg);
+ break;
+ case 'p':
+ printout = TRUE;
+ break;
+ case 'r':
+ random = TRUE;
+ break;
+ case 's':
+ speedonly = TRUE;
+ break;
+ case 'v':
+ variance = bitorbyte_atof(optarg);
+ break;
+ case 'w':
+ binwidth = bitorbyte_atof(optarg);
+ break;
+ case '?':
+ default:
+ fprintf(stderr,"Usage -b bins, -c count, -l log normal, -m mean, -p print, -s speed only, -v variance");
+ exit(-1);
+ }
+ if (bincount > MAXBINS) {
+ int max = MAXBINS;
+ fprintf(stderr, "Maximum number of bins is %d while %d requested\n", max, bincount);
+ abort();
+ }
+ /* Intializes random number generator */
+ if (random) {
+ srand((unsigned) time(&t));
+ printf("seed = %ld\n", t);
+ }
+ memset(histogram, 0, sizeof(histogram));
+#ifdef HAVE_CLOCK_GETTIME
+ struct timespec t1;
+ clock_gettime(CLOCK_REALTIME, &t1);
+#else
+ struct timeval t1;
+ gettimeofday( &t1, NULL );
+#endif
+ if (gaussian) {
+ for( i = 0 ; i < count ; i++ ) {
+ int result = round(normal(mean,variance)/binwidth);
+ if (!speedonly) {
+ if (result >= 0 && result < (MAXBINS - 1)) {
+ histogram[result]++;
+ if (result < minbin)
+ minbin = result;
+ if (result > maxbin)
+ maxbin = result;
+ }
+ }
+ }
+ } else {
+ for( i = 0 ; i < count ; i++ ) {
+ int result = round(lognormal(mean,variance)/binwidth);
+ if (!speedonly) {
+ if (result >= 0 && result < (MAXBINS - 1)) {
+ histogram[result]++;
+ if (result < minbin)
+ minbin = result;
+ if (result > maxbin)
+ maxbin = result;
+ }
+ }
+ }
+ }
+#ifdef HAVE_CLOCK_GETTIME
+ struct timespec t2;
+ clock_gettime(CLOCK_REALTIME, &t2);
+ total = timespec_diff(t2, t1);
+#else
+ struct timeval t2;
+ gettimeofday( &t2, NULL );
+ total = timeval_diff(t2, t1);
+#endif
+ if (printout) {
+ for( i = minbin ; i <= maxbin ; i++ )
+ printf("%.0f %d\n", i * binwidth, histogram[i]);
+ }
+ exectime = round(1e9 * total / count);
+ if (!printout) {
+ printf("Total time=%f secs, count= %d, average generate time of %d nanoseconds\n", total, count, exectime);
+ }
+ return(0);
+}
+#ifdef HAVE_CLOCK_GETTIME
+// tv1 assumed greater than tv0
+static double timespec_diff (struct timespec tv1, struct timespec tv0) {
+ double result;
+ if (tv1.tv_nsec < tv0.tv_nsec) {
+ tv1.tv_nsec += BILLION;
+ tv1.tv_sec--;
+ }
+ result = (double) (((tv1.tv_sec - tv0.tv_sec) * BILLION) + (tv1.tv_nsec - tv0.tv_nsec));
+ return (result / 1e9);
+}
+#else
+// tv1 assumed greater than tv0
+static double timeval_diff (struct timeval tv1, struct timeval tv0) {
+ double result;
+ if (tv1.tv_usec < tv0.tv_usec) {
+ tv1.tv_usec += MILLION;
+ tv1.tv_sec--;
+ }
+ result = (double) (((tv1.tv_sec - tv0.tv_sec) * MILLION) + (tv1.tv_usec - tv0.tv_usec));
+ return (result / 1e6);
+}
+#endif
diff --git a/src/checksums.c b/src/checksums.c
new file mode 100644
index 0000000..852f195
--- /dev/null
+++ b/src/checksums.c
@@ -0,0 +1,229 @@
+/*---------------------------------------------------------------
+ * Copyright (c) 2018
+ * Broadcom Corporation
+ * All Rights Reserved.
+ *---------------------------------------------------------------
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software 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 name of Broadcom Coporation,
+ * 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.
+ * ________________________________________________________________
+ *
+ * UDP checksums v4 and v6
+ *
+ * The checksum calculation is defined in RFC 768
+ * hints as to how to calculate it efficiently are in RFC 1071
+ *
+ * by Robert J. McMahon (rjmcmahon@rjmcmahon.com, bob.mcmahon@broadcom.com)
+ * -------------------------------------------------------------------
+ */
+#include "headers.h"
+
+/*
+ *
+ * Compute Internet Checksum for UDP packets (assumes 32 bit system)
+ *
+ * IPV4 Notes:
+ *
+ * User Datagram Header Format
+ * 0 7 8 15 16 23 24 31
+ * +--------+--------+--------+--------+
+ * | Source | Destination |
+ * | Port | Port |
+ * +--------+--------+--------+--------+
+ * | | |
+ * | Length | Checksum |
+ * +--------+--------+--------+--------+
+ * |
+ * | data octets ...
+ * +---------------- ...
+ *
+ *
+ * Checksum is the 16-bit one's complement of the one's complement sum of a
+ * pseudo header of information from the IP header, the UDP header, and the
+ * data, padded with zero octets at the end (if necessary) to make a
+ * multiple of two octets.
+ *
+ * The ipv4 pseudo header conceptually prefixed to the UDP header contains the
+ * source address, the destination address, the protocol, and the UDP
+ * length. This information gives protection against misrouted datagrams.
+ * This checksum procedure is the same as is used in TCP.
+ *
+ * 0 7 8 15 16 23 24 31
+ * +--------+--------+--------+--------+
+ * | source address |
+ * +--------+--------+--------+--------+
+ * | destination address |
+ * +--------+--------+--------+--------+
+ * | zero |protocol| UDP length |
+ * +--------+--------+--------+--------+
+ *
+ * If the computed checksum is zero, it is transmitted as all ones (the
+ * equivalent in one's complement arithmetic). An all zero transmitted
+ * checksum value means that the transmitter generated no checksum (for
+ * debugging or for higher level protocols that don't care).
+ *
+ *
+ * IPV6 Notes:
+ *
+ * Any transport or other upper-layer protocol that includes the
+ * addresses from the IP header in its checksum computation must be
+ * modified for use over IPv6, to include the 128-bit IPv6 addresses
+ * instead of 32-bit IPv4 addresses. In particular, the following
+ * illustration shows the TCP and UDP "pseudo-header" for IPv6:
+ *
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * + +
+ * | |
+ * + Source Address +
+ * | |
+ * + +
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * + +
+ * | |
+ * + Destination Address +
+ * | |
+ * + +
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Upper-Layer Packet Length |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | zero | Next Header |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * o If the IPv6 packet contains a Routing header, the Destination
+ * Address used in the pseudo-header is that of the final
+ * destination. At the originating node, that address will be in
+ * the last element of the Routing header; at the recipient(s),
+ * that address will be in the Destination Address field of the
+ * IPv6 header.
+ *
+ * o The Next Header value in the pseudo-header identifies the
+ * upper-layer protocol (e.g., 6 for TCP, or 17 for UDP). It will
+ * differ from the Next Header value in the IPv6 header if there
+ * are extension headers between the IPv6 header and the upper-
+ * layer header.
+ *
+ * o The Upper-Layer Packet Length in the pseudo-header is the
+ * length of the upper-layer header and data (e.g., TCP header
+ * plus TCP data). Some upper-layer protocols carry their own
+ * length information (e.g., the Length field in the UDP header);
+ * for such protocols, that is the length used in the pseudo-
+ * header. Other protocols (such as TCP) do not carry their own
+ * length information, in which case the length used in the
+ * pseudo-header is the Payload Length from the IPv6 header, minus
+ * the length of any extension headers present between the IPv6
+ * header and the upper-layer header.
+ *
+ * o Unlike IPv4, when UDP packets are originated by an IPv6 node,
+ * the UDP checksum is not optional. That is, whenever
+ * originating a UDP packet, an IPv6 node must compute a UDP
+ * checksum over the packet and the pseudo-header, and, if that
+ * computation yields a result of zero, it must be changed to hex
+ * FFFF for placement in the UDP header. IPv6 receivers must
+ * discard UDP packets containing a zero checksum, and should log
+ * the error.
+ *
+ *
+ * Returns zero on checksum success, non zero otherwise
+ */
+
+#define IPV4SRCOFFSET 12 // the ipv4 source address offset from the l3 pdu
+#define IPV6SRCOFFSET 8 // the ipv6 source address offset
+#define IPV6SIZE 8 // units is number of 16 bits, i.e. 128 bits is eight 16 bits
+#define IPV4SIZE 2 // v4 is two 16 bits (32 bits)
+#define UDPPROTO 17 // UDP protocol value for psuedo header
+uint32_t udpchecksum(const void *l3pdu, const void *l4pdu, int udplen, int v6) {
+ register uint32_t sum = 0;
+ const uint16_t *data;
+ int i;
+
+ const struct udphdr *udp_hdr = (const struct udphdr *)l4pdu;
+ if (!udp_hdr->check) {
+ if (v6)
+ // v6 requires checksums
+ return -1;
+ else
+ // v4 checksums are optional
+ return 0;
+ }
+
+ /*
+ * Build pseudo headers, partially from the packet
+ * (which are in network byte order) and
+ * the protocol of UDP (value of 17). Also, the IP dst
+ * addr immediately follows the src so double the size
+ * per each loop to cover both addrs
+ */
+ if (v6) {
+ // skip to the ip header v6 src field, offset 8 (see ipv6 header)
+ data = (const uint16_t *)((char *)l3pdu + IPV6SRCOFFSET);
+ for (i = 0; i < (2 * IPV6SIZE); i++) {
+ sum += *data++;
+ }
+ } else {
+ // skip to the ip header v4 src field, offset 12 (see ipv4 header)
+ data = (const uint16_t *)((char *)l3pdu + IPV4SRCOFFSET);
+ for (i = 0; i < (2 * IPV4SIZE); i++) {
+ sum += *data++;
+ }
+ }
+ // These should work for both v4 and v6 even though
+ // v6 psuedo header uses 32 bit values because the
+ // uppers in v6 will be zero
+ sum += htons(UDPPROTO); // proto of UDP is 17
+ sum += htons(udplen); // For UDP, the pseudo hdr len equals udp len
+
+ /*
+ * UDP hdr + payload
+ */
+ data = (const uint16_t *) l4pdu;
+ while( udplen > 1 ) {
+ sum += *data++;
+ udplen -= 2;
+ }
+ /* Add left-over byte, if any */
+ if( udplen > 0 )
+ sum += * (uint8_t *) data;
+
+ /* Fold 32-bit sum to 16 bits */
+ while (sum>>16)
+ sum = (sum & 0xffff) + (sum >> 16);
+
+ /* return ones complement */
+ sum = (sum ^ 0xffff);
+ return sum;
+}
diff --git a/src/dscp.c b/src/dscp.c
new file mode 100644
index 0000000..de691a7
--- /dev/null
+++ b/src/dscp.c
@@ -0,0 +1,180 @@
+/* DSCP lookup routines lifted from openssh, modified a bit for WiFi WMMM
+ *
+ * Copyright (c) 2000 Markus Friedl. All rights reserved.
+ * Copyright (c) 2005,2006 Damien Miller. All rights reserved.
+ * Copyright (c) 2022 Robert McMahon. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#if HAVE_STRING_H
+#include <string.h>
+#endif
+#if HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#if HAVE_INTTYPES_H
+#include <inttypes.h>
+#endif
+
+#ifdef WIN32
+#define strcasecmp(a,b) _stricmp(a,b)
+#define snprintf _snprintf
+#endif
+
+#include "dscp.h"
+
+/*
+ * Definitions for IP type of service (ip_tos)
+ */
+
+#if HAVE_NETINET_IN_SYSTM_H
+#if defined(__FreeBSD__)
+#include <sys/types.h>
+#endif
+#include <netinet/in_systm.h>
+#endif
+#if HAVE_NETINET_IP_H
+#include <netinet/ip.h>
+#endif
+
+#ifndef IPTOS_LOWDELAY
+# define IPTOS_LOWDELAY 0x10
+# define IPTOS_THROUGHPUT 0x08
+# define IPTOS_RELIABILITY 0x04
+# define IPTOS_LOWCOST 0x02
+# define IPTOS_MINCOST IPTOS_LOWCOST
+#endif /* IPTOS_LOWDELAY */
+
+/*
+ * Definitions for DiffServ Codepoints as per RFC2474, rfc8622, rfc8325
+ */
+#ifndef IPTOS_DSCP_AF11
+# define IPTOS_DSCP_AF11 0x28
+# define IPTOS_DSCP_AF12 0x30
+# define IPTOS_DSCP_AF13 0x38
+# define IPTOS_DSCP_AF21 0x48
+# define IPTOS_DSCP_AF22 0x50
+# define IPTOS_DSCP_AF23 0x58
+# define IPTOS_DSCP_AF31 0x68
+# define IPTOS_DSCP_AF32 0x70
+# define IPTOS_DSCP_AF33 0x78
+# define IPTOS_DSCP_AF41 0x88
+# define IPTOS_DSCP_AF42 0x90
+# define IPTOS_DSCP_AF43 0x98
+# define IPTOS_DSCP_EF 0xb8
+# define IPTOS_DSCP_LE 0x01 // rfc8622
+# define IPTOS_DSCP_NQB 0x05 // rfc8325
+# define IPTOS_DSCP_NQB2 0x2D // rfc8325 45 decimal
+
+#endif /* IPTOS_DSCP_AF11 */
+
+#ifndef IPTOS_DSCP_CS0
+# define IPTOS_DSCP_CS0 0x00
+# define IPTOS_DSCP_CS1 0x20
+# define IPTOS_DSCP_CS2 0x40
+# define IPTOS_DSCP_CS3 0x60
+# define IPTOS_DSCP_CS4 0x80
+# define IPTOS_DSCP_CS5 0xa0
+# define IPTOS_DSCP_CS6 0xc0
+# define IPTOS_DSCP_CS7 0xe0
+#endif /* IPTOS_DSCP_CS0 */
+#ifndef IPTOS_DSCP_EF
+# define IPTOS_DSCP_EF 0xb8
+#endif /* IPTOS_DSCP_EF */
+
+static const struct {
+ const char *name;
+ int value;
+} ipqos[] = {
+ { "af11", IPTOS_DSCP_AF11 },
+ { "af12", IPTOS_DSCP_AF12 },
+ { "af13", IPTOS_DSCP_AF13 },
+ { "af21", IPTOS_DSCP_AF21 },
+ { "af22", IPTOS_DSCP_AF22 },
+ { "af23", IPTOS_DSCP_AF23 },
+ { "af31", IPTOS_DSCP_AF31 },
+ { "af32", IPTOS_DSCP_AF32 },
+ { "af33", IPTOS_DSCP_AF33 },
+ { "af41", IPTOS_DSCP_AF41 },
+ { "af42", IPTOS_DSCP_AF42 },
+ { "af43", IPTOS_DSCP_AF43 },
+ { "cs0", IPTOS_DSCP_CS0 },
+ { "cs1", IPTOS_DSCP_CS1 },
+ { "cs2", IPTOS_DSCP_CS2 },
+ { "cs3", IPTOS_DSCP_CS3 },
+ { "cs4", IPTOS_DSCP_CS4 },
+ { "cs5", IPTOS_DSCP_CS5 },
+ { "cs6", IPTOS_DSCP_CS6 },
+ { "cs7", IPTOS_DSCP_CS7 },
+ { "ef", IPTOS_DSCP_EF },
+ { "le", IPTOS_DSCP_LE },
+ { "nqb", IPTOS_DSCP_NQB },
+ { "nqb2", IPTOS_DSCP_NQB2 },
+ { "ac_be", IPTOS_DSCP_CS0 },
+ { "ac_bk", IPTOS_DSCP_CS1 },
+ { "ac_vi", IPTOS_DSCP_CS4 },
+ { "ac_vo", IPTOS_DSCP_EF },
+ { "lowdelay", IPTOS_LOWDELAY },
+ { "throughput", IPTOS_THROUGHPUT },
+ { "reliability", IPTOS_RELIABILITY },
+ { NULL, -1 }
+};
+
+int
+parse_ipqos(const char *cp)
+{
+ unsigned int i;
+ char *ep = NULL;
+ long val;
+
+ if (cp == NULL)
+ return -1;
+ for (i = 0; ipqos[i].name != NULL; i++) {
+ if (strcasecmp(cp, ipqos[i].name) == 0)
+ return ipqos[i].value;
+ }
+ /* Try parsing as an integer */
+ val = strtol(cp, &ep, 0);
+ if (*cp == '\0' || *ep != '\0' || val < 0 || val > 255)
+ return -1;
+ return val;
+}
+
+const char *
+iptos2str(int iptos)
+{
+ int i;
+ static char iptos_str[sizeof "0xff"];
+ if (iptos < 0 || iptos > 64) iptos = 0;
+ for (i = 0; ipqos[i].name != NULL; i++) {
+ if (ipqos[i].value == iptos)
+ return ipqos[i].name;
+ }
+ snprintf(iptos_str, sizeof iptos_str, "0x%02x", iptos);
+ return iptos_str;
+}
diff --git a/src/gnu_getopt.c b/src/gnu_getopt.c
new file mode 100644
index 0000000..6905278
--- /dev/null
+++ b/src/gnu_getopt.c
@@ -0,0 +1,818 @@
+/* Getopt for GNU.
+ NOTE: gnu_getopt is now part of the C library, so if you don't know what
+ "Keep this file name-space clean" means, talk to roland@gnu.ai.mit.edu
+ before changing it!
+
+ Copyright (C) 1987, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97
+ Free Software Foundation, Inc.
+
+ This file is part of the GNU C Library. Its master source is NOT part of
+ the C library, however. The master source lives in /gd/gnu/lib.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The GNU C Library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the GNU C Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+/*
+ * modified July 9, 1999 by mark gates <mgates@nlanr.net>
+ * Dec 17, 1999
+ *
+ * renamed all functions and variables by prepending "gnu_"
+ * removed/redid a bunch of stuff under the assumption we're
+ * using a modern standard C compiler.
+ * add #include <string.h> here for strncmp(). Originally
+ * it was included only under special conditions.
+ *
+ * $Id: gnu_getopt.c,v 1.1.1.1 2004/05/18 01:50:44 kgibbs Exp $
+ */
+
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#ifndef _MSC_VER /* Visual C++ doesn't have unistd.h */
+ #include <unistd.h>
+#endif
+#include <string.h>
+
+#ifndef _
+/* This is for other GNU distributions with internationalized messages.
+ When compiling libc, the _ macro is predefined. */
+ #ifdef HAVE_LIBINTL_H
+ #include <libintl.h>
+ #define _(msgid) gettext (msgid)
+ #else
+ #define _(msgid) (msgid)
+ #endif
+#endif
+
+/* This version of `gnu_getopt' appears to the caller like standard
+ Unix `getopt' but it behaves differently for the user, since it
+ allows the user to intersperse the options with the other
+ arguments.
+
+ As `gnu_getopt' works, it permutes the elements of ARGV so that,
+ when it is done, all the options precede everything else. Thus
+ all application programs are extended to handle flexible argument order.
+
+ Setting the environment variable POSIXLY_CORRECT disables permutation.
+ Then the behavior is completely standard.
+
+ GNU application programs can use a third alternative mode in which
+ they can distinguish the relative order of options and other arguments. */
+
+#include "gnu_getopt.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* For communication from `gnu_getopt' to the caller.
+ When `gnu_getopt' finds an option that takes an argument,
+ the argument value is returned here.
+ Also, when `ordering' is RETURN_IN_ORDER,
+ each non-option ARGV-element is returned here. */
+
+char *gnu_optarg = NULL;
+
+/* Index in ARGV of the next element to be scanned.
+ This is used for communication to and from the caller
+ and for communication between successive calls to `gnu_getopt'.
+
+ On entry to `gnu_getopt', zero means this is the first call; initialize.
+
+ When `gnu_getopt' returns -1, this is the index of the first of the
+ non-option elements that the caller should itself scan.
+
+ Otherwise, `gnu_optind' communicates from one call to the next
+ how much of ARGV has been scanned so far. */
+
+/* 1003.2 says this must be 1 before any call. */
+int gnu_optind = 1;
+
+/* Formerly, initialization of gnu_getopt depended on gnu_optind==0, which
+ causes problems with re-calling gnu_getopt as programs generally don't
+ know that. */
+
+int __gnu_getopt_initialized = 0;
+
+/* The next char to be scanned in the option-element
+ in which the last option character we returned was found.
+ This allows us to pick up the scan where we left off.
+
+ If this is zero, or a null string, it means resume the scan
+ by advancing to the next ARGV-element. */
+
+static char *nextchar;
+
+/* Callers store zero here to inhibit the error message
+ for unrecognized options. */
+
+int gnu_opterr = 1;
+
+/* Set to an option character which was unrecognized.
+ This must be initialized on some systems to avoid linking in the
+ system's own gnu_getopt implementation. */
+
+int gnu_optopt = '?';
+
+/* Describe how to deal with options that follow non-option ARGV-elements.
+
+ If the caller did not specify anything,
+ the default is REQUIRE_ORDER if the environment variable
+ POSIXLY_CORRECT is defined, PERMUTE otherwise.
+
+ REQUIRE_ORDER means don't recognize them as options;
+ stop option processing when the first non-option is seen.
+ This is what Unix does.
+ This mode of operation is selected by either setting the environment
+ variable POSIXLY_CORRECT, or using `+' as the first character
+ of the list of option characters.
+
+ PERMUTE is the default. We permute the contents of ARGV as we scan,
+ so that eventually all the non-options are at the end. This allows options
+ to be given in any order, even with programs that were not written to
+ expect this.
+
+ RETURN_IN_ORDER is an option available to programs that were written
+ to expect options and other ARGV-elements in any order and that care about
+ the ordering of the two. We describe each non-option ARGV-element
+ as if it were the argument of an option with character code 1.
+ Using `-' as the first character of the list of option characters
+ selects this mode of operation.
+
+ The special argument `--' forces an end of option-scanning regardless
+ of the value of `ordering'. In the case of RETURN_IN_ORDER, only
+ `--' can cause `gnu_getopt' to return -1 with `gnu_optind' != ARGC. */
+
+static enum {
+ REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER
+} ordering;
+
+/* Value of POSIXLY_CORRECT environment variable. */
+static char *posixly_correct;
+
+
+/* Avoid depending on library functions or files
+ whose names are inconsistent. */
+
+static char *
+my_index( const char* str, int chr ) {
+ while ( *str ) {
+ if ( *str == chr )
+ return(char *) str;
+ str++;
+ }
+ return 0;
+}
+
+
+/* Handle permutation of arguments. */
+
+/* Describe the part of ARGV that contains non-options that have
+ been skipped. `first_nonopt' is the index in ARGV of the first of them;
+ `last_nonopt' is the index after the last of them. */
+
+static int first_nonopt;
+static int last_nonopt;
+
+/* Exchange two adjacent subsequences of ARGV.
+ One subsequence is elements [first_nonopt,last_nonopt)
+ which contains all the non-options that have been skipped so far.
+ The other is elements [last_nonopt,gnu_optind), which contains all
+ the options processed since those non-options were skipped.
+
+ `first_nonopt' and `last_nonopt' are relocated so that they describe
+ the new indices of the non-options in ARGV after they are moved. */
+
+static void exchange( char **argv );
+
+static void
+exchange( char **argv ) {
+ int bottom = first_nonopt;
+ int middle = last_nonopt;
+ int top = gnu_optind;
+ char *tem;
+
+ /* Exchange the shorter segment with the far end of the longer segment.
+ That puts the shorter segment into the right place.
+ It leaves the longer segment in the right place overall,
+ but it consists of two parts that need to be swapped next. */
+
+ while ( top > middle && middle > bottom ) {
+ if ( top - middle > middle - bottom ) {
+ /* Bottom segment is the short one. */
+ int len = middle - bottom;
+ register int i;
+
+ /* Swap it with the top part of the top segment. */
+ for ( i = 0; i < len; i++ ) {
+ tem = argv[bottom + i];
+ argv[bottom + i] = argv[top - (middle - bottom) + i];
+ argv[top - (middle - bottom) + i] = tem;
+ }
+ /* Exclude the moved bottom segment from further swapping. */
+ top -= len;
+ } else {
+ /* Top segment is the short one. */
+ int len = top - middle;
+ register int i;
+
+ /* Swap it with the bottom part of the bottom segment. */
+ for ( i = 0; i < len; i++ ) {
+ tem = argv[bottom + i];
+ argv[bottom + i] = argv[middle + i];
+ argv[middle + i] = tem;
+ }
+ /* Exclude the moved top segment from further swapping. */
+ bottom += len;
+ }
+ }
+
+ /* Update records for the slots the non-options now occupy. */
+
+ first_nonopt += (gnu_optind - last_nonopt);
+ last_nonopt = gnu_optind;
+}
+
+/* Initialize the internal data when the first call is made. */
+
+static const char *
+_gnu_getopt_initialize( int argc,
+ char *const * argv,
+ const char *optstring );
+
+static const char *
+_gnu_getopt_initialize( int argc,
+ char *const * argv,
+ const char *optstring ) {
+ /* Start processing options with ARGV-element 1 (since ARGV-element 0
+ is the program name); the sequence of previously skipped
+ non-option ARGV-elements is empty. */
+
+ first_nonopt = last_nonopt = gnu_optind = 1;
+
+ nextchar = NULL;
+
+ posixly_correct = getenv ("POSIXLY_CORRECT");
+
+ /* Determine how to handle the ordering of options and nonoptions. */
+
+ if ( optstring[0] == '-' ) {
+ ordering = RETURN_IN_ORDER;
+ ++optstring;
+ } else if ( optstring[0] == '+' ) {
+ ordering = REQUIRE_ORDER;
+ ++optstring;
+ } else if ( posixly_correct != NULL )
+ ordering = REQUIRE_ORDER;
+ else
+ ordering = PERMUTE;
+
+ return optstring;
+}
+
+/* Scan elements of ARGV (whose length is ARGC) for option characters
+ given in OPTSTRING.
+
+ If an element of ARGV starts with '-', and is not exactly "-" or "--",
+ then it is an option element. The characters of this element
+ (aside from the initial '-') are option characters. If `gnu_getopt'
+ is called repeatedly, it returns successively each of the option characters
+ from each of the option elements.
+
+ If `gnu_getopt' finds another option character, it returns that character,
+ updating `gnu_optind' and `nextchar' so that the next call to `gnu_getopt' can
+ resume the scan with the following option character or ARGV-element.
+
+ If there are no more option characters, `gnu_getopt' returns -1.
+ Then `gnu_optind' is the index in ARGV of the first ARGV-element
+ that is not an option. (The ARGV-elements have been permuted
+ so that those that are not options now come last.)
+
+ OPTSTRING is a string containing the legitimate option characters.
+ If an option character is seen that is not listed in OPTSTRING,
+ return '?' after printing an error message. If you set `gnu_opterr' to
+ zero, the error message is suppressed but we still return '?'.
+
+ If a char in OPTSTRING is followed by a colon, that means it wants an arg,
+ so the following text in the same ARGV-element, or the text of the following
+ ARGV-element, is returned in `gnu_optarg'. Two colons mean an option that
+ wants an optional arg; if there is text in the current ARGV-element,
+ it is returned in `gnu_optarg', otherwise `gnu_optarg' is set to zero.
+
+ If OPTSTRING starts with `-' or `+', it requests different methods of
+ handling the non-option ARGV-elements.
+ See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above.
+
+ Long-named options begin with `--' instead of `-'.
+ Their names may be abbreviated as long as the abbreviation is unique
+ or is an exact match for some defined option. If they have an
+ argument, it follows the option name in the same ARGV-element, separated
+ from the option name by a `=', or else the in next ARGV-element.
+ When `gnu_getopt' finds a long-named option, it returns 0 if that option's
+ `flag' field is nonzero, the value of the option's `val' field
+ if the `flag' field is zero.
+
+ The elements of ARGV aren't really const, because we permute them.
+ But we pretend they're const in the prototype to be compatible
+ with other systems.
+
+ LONGOPTS is a vector of `struct option' terminated by an
+ element containing a name which is zero.
+
+ LONGIND returns the index in LONGOPT of the long-named option found.
+ It is only valid when a long-named option has been found by the most
+ recent call.
+
+ If LONG_ONLY is nonzero, '-' as well as '--' can introduce
+ long-named options. */
+
+int
+_gnu_getopt_internal( int argc,
+ char *const *argv,
+ const char *optstring,
+ const struct option *longopts,
+ int *longind,
+ int long_only ) {
+ gnu_optarg = NULL;
+
+ if ( !__gnu_getopt_initialized || gnu_optind == 0 ) {
+ optstring = _gnu_getopt_initialize (argc, argv, optstring);
+ gnu_optind = 1; /* Don't scan ARGV[0], the program name. */
+ __gnu_getopt_initialized = 1;
+ }
+
+ /* Test whether ARGV[gnu_optind] points to a non-option argument.
+ Either it does not have option syntax, or there is an environment flag
+ from the shell indicating it is not an option. The later information
+ is only used when the used in the GNU libc. */
+
+#define NONOPTION_P (argv[gnu_optind][0] != '-' || argv[gnu_optind][1] == '\0')
+
+ if ( nextchar == NULL || *nextchar == '\0' ) {
+ /* Advance to the next ARGV-element. */
+
+ /* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has been
+ moved back by the user (who may also have changed the arguments). */
+ if ( last_nonopt > gnu_optind )
+ last_nonopt = gnu_optind;
+ if ( first_nonopt > gnu_optind )
+ first_nonopt = gnu_optind;
+
+ if ( ordering == PERMUTE ) {
+ /* If we have just processed some options following some non-options,
+ exchange them so that the options come first. */
+
+ if ( first_nonopt != last_nonopt && last_nonopt != gnu_optind )
+ exchange ((char **) argv);
+ else if ( last_nonopt != gnu_optind )
+ first_nonopt = gnu_optind;
+
+ /* Skip any additional non-options
+ and extend the range of non-options previously skipped. */
+
+ while ( gnu_optind < argc && NONOPTION_P )
+ gnu_optind++;
+ last_nonopt = gnu_optind;
+ }
+
+ /* The special ARGV-element `--' means premature end of options.
+ Skip it like a null option,
+ then exchange with previous non-options as if it were an option,
+ then skip everything else like a non-option. */
+
+ if ( gnu_optind != argc && !strcmp (argv[gnu_optind], "--") ) {
+ gnu_optind++;
+
+ if ( first_nonopt != last_nonopt && last_nonopt != gnu_optind )
+ exchange ((char **) argv);
+ else if ( first_nonopt == last_nonopt )
+ first_nonopt = gnu_optind;
+ last_nonopt = argc;
+
+ gnu_optind = argc;
+ }
+
+ /* If we have done all the ARGV-elements, stop the scan
+ and back over any non-options that we skipped and permuted. */
+
+ if ( gnu_optind == argc ) {
+ /* Set the next-arg-index to point at the non-options
+ that we previously skipped, so the caller will digest them. */
+ if ( first_nonopt != last_nonopt )
+ gnu_optind = first_nonopt;
+ return -1;
+ }
+
+ /* If we have come to a non-option and did not permute it,
+ either stop the scan or describe it to the caller and pass it by. */
+
+ if ( NONOPTION_P ) {
+ if ( ordering == REQUIRE_ORDER )
+ return -1;
+ gnu_optarg = argv[gnu_optind++];
+ return 1;
+ }
+
+ /* We have found another option-ARGV-element.
+ Skip the initial punctuation. */
+
+ nextchar = (argv[gnu_optind] + 1
+ + (longopts != NULL && argv[gnu_optind][1] == '-'));
+ }
+
+ /* Decode the current option-ARGV-element. */
+
+ /* Check whether the ARGV-element is a long option.
+
+ If long_only and the ARGV-element has the form "-f", where f is
+ a valid short option, don't consider it an abbreviated form of
+ a long option that starts with f. Otherwise there would be no
+ way to give the -f short option.
+
+ On the other hand, if there's a long option "fubar" and
+ the ARGV-element is "-fu", do consider that an abbreviation of
+ the long option, just like "--fu", and not "-f" with arg "u".
+
+ This distinction seems to be the most useful approach. */
+
+ if ( longopts != NULL
+ && (argv[gnu_optind][1] == '-'
+ || (long_only && (argv[gnu_optind][2] || !my_index (optstring, argv[gnu_optind][1])))) ) {
+ char *nameend;
+ const struct option *p;
+ const struct option *pfound = NULL;
+ int exact = 0;
+ int ambig = 0;
+ int indfound = -1;
+ int option_index;
+
+ for ( nameend = nextchar; *nameend && *nameend != '='; nameend++ )
+ /* Do nothing. */ ;
+
+ /* Test all long options for either exact match
+ or abbreviated matches. */
+ for ( p = longopts, option_index = 0; p->name; p++, option_index++ )
+ if ( !strncmp (p->name, nextchar, nameend - nextchar) ) {
+ if ( (unsigned int) (nameend - nextchar)
+ == (unsigned int) strlen (p->name) ) {
+ /* Exact match found. */
+ pfound = p;
+ indfound = option_index;
+ exact = 1;
+ break;
+ } else if ( pfound == NULL ) {
+ /* First nonexact match found. */
+ pfound = p;
+ indfound = option_index;
+ } else
+ /* Second or later nonexact match found. */
+ ambig = 1;
+ }
+
+ if ( ambig && !exact ) {
+ if ( gnu_opterr )
+ fprintf (stderr, _("%s: option `%s' is ambiguous\n"),
+ argv[0], argv[gnu_optind]);
+ nextchar += strlen (nextchar);
+ gnu_optind++;
+ gnu_optopt = 0;
+ return '?';
+ }
+
+ if ( pfound != NULL ) {
+ option_index = indfound;
+ gnu_optind++;
+ if ( *nameend ) {
+ /* Don't test has_arg with >, because some C compilers don't
+ allow it to be used on enums. */
+ if ( pfound->has_arg )
+ gnu_optarg = nameend + 1;
+ else {
+ if ( gnu_opterr ) {
+ if ( argv[gnu_optind - 1][1] == '-' ) {
+ /* --option */
+ fprintf (stderr,
+ _("%s: option `--%s' doesn't allow an argument\n"),
+ argv[0], pfound->name);
+ } else {
+ /* +option or -option */
+ fprintf (stderr,
+ _("%s: option `%c%s' doesn't allow an argument\n"),
+ argv[0], argv[gnu_optind - 1][0], pfound->name);
+ }
+ }
+
+ nextchar += strlen (nextchar);
+
+ gnu_optopt = pfound->val;
+ return '?';
+ }
+ } else if ( pfound->has_arg == 1 ) {
+ if ( gnu_optind < argc )
+ gnu_optarg = argv[gnu_optind++];
+ else {
+ if ( gnu_opterr )
+ fprintf (stderr,
+ _("%s: option `%s' requires an argument\n"),
+ argv[0], argv[gnu_optind - 1]);
+ nextchar += strlen (nextchar);
+ gnu_optopt = pfound->val;
+ return optstring[0] == ':' ? ':' : '?';
+ }
+ }
+ nextchar += strlen (nextchar);
+ if ( longind != NULL )
+ *longind = option_index;
+ if ( pfound->flag ) {
+ *(pfound->flag) = pfound->val;
+ return 0;
+ }
+ return pfound->val;
+ }
+
+ /* Can't find it as a long option. If this is not gnu_getopt_long_only,
+ or the option starts with '--' or is not a valid short
+ option, then it's an error.
+ Otherwise interpret it as a short option. */
+ if ( !long_only || argv[gnu_optind][1] == '-'
+ || my_index (optstring, *nextchar) == NULL ) {
+ if ( gnu_opterr ) {
+ if ( argv[gnu_optind][1] == '-' )
+ /* --option */
+ fprintf (stderr, _("%s: unrecognized option `--%s'\n"),
+ argv[0], nextchar);
+ else
+ /* +option or -option */
+ fprintf (stderr, _("%s: unrecognized option `%c%s'\n"),
+ argv[0], argv[gnu_optind][0], nextchar);
+ }
+ nextchar = (char *) "";
+ gnu_optind++;
+ gnu_optopt = 0;
+ return '?';
+ }
+ }
+
+ /* Look at and handle the next short option-character. */
+
+ {
+ char c = *nextchar++;
+ char *temp = my_index (optstring, c);
+
+ /* Increment `gnu_optind' when we start to process its last character. */
+ if ( *nextchar == '\0' )
+ ++gnu_optind;
+
+ if ( temp == NULL || c == ':' ) {
+ if ( gnu_opterr ) {
+ if ( posixly_correct )
+ /* 1003.2 specifies the format of this message. */
+ fprintf (stderr, _("%s: illegal option -- %c\n"),
+ argv[0], c);
+ else
+ fprintf (stderr, _("%s: invalid option -- %c\n"),
+ argv[0], c);
+ }
+ gnu_optopt = c;
+ return '?';
+ }
+ /* Convenience. Treat POSIX -W foo same as long option --foo */
+ if ( temp[0] == 'W' && temp[1] == ';' ) {
+ char *nameend;
+ const struct option *p;
+ const struct option *pfound = NULL;
+ int exact = 0;
+ int ambig = 0;
+ int indfound = 0;
+ int option_index;
+
+ /* This is an option that requires an argument. */
+ if ( *nextchar != '\0' ) {
+ gnu_optarg = nextchar;
+ /* If we end this ARGV-element by taking the rest as an arg,
+ we must advance to the next element now. */
+ gnu_optind++;
+ } else if ( gnu_optind == argc ) {
+ if ( gnu_opterr ) {
+ /* 1003.2 specifies the format of this message. */
+ fprintf (stderr, _("%s: option requires an argument -- %c\n"),
+ argv[0], c);
+ }
+ gnu_optopt = c;
+ if ( optstring[0] == ':' )
+ c = ':';
+ else
+ c = '?';
+ return c;
+ } else
+ /* We already incremented `gnu_optind' once;
+ increment it again when taking next ARGV-elt as argument. */
+ gnu_optarg = argv[gnu_optind++];
+
+ /* gnu_optarg is now the argument, see if it's in the
+ table of longopts. */
+
+ for ( nextchar = nameend = gnu_optarg; *nameend && *nameend != '='; nameend++ )
+ /* Do nothing. */ ;
+
+ /* Test all long options for either exact match
+ or abbreviated matches. */
+ for ( p = longopts, option_index = 0; p->name; p++, option_index++ )
+ if ( !strncmp (p->name, nextchar, nameend - nextchar) ) {
+ if ( (unsigned int) (nameend - nextchar) == strlen (p->name) ) {
+ /* Exact match found. */
+ pfound = p;
+ indfound = option_index;
+ exact = 1;
+ break;
+ } else if ( pfound == NULL ) {
+ /* First nonexact match found. */
+ pfound = p;
+ indfound = option_index;
+ } else
+ /* Second or later nonexact match found. */
+ ambig = 1;
+ }
+ if ( ambig && !exact ) {
+ if ( gnu_opterr )
+ fprintf (stderr, _("%s: option `-W %s' is ambiguous\n"),
+ argv[0], argv[gnu_optind]);
+ nextchar += strlen (nextchar);
+ gnu_optind++;
+ return '?';
+ }
+ if ( pfound != NULL ) {
+ option_index = indfound;
+ if ( *nameend ) {
+ /* Don't test has_arg with >, because some C compilers don't
+ allow it to be used on enums. */
+ if ( pfound->has_arg )
+ gnu_optarg = nameend + 1;
+ else {
+ if ( gnu_opterr )
+ fprintf (stderr, _("\
+%s: option `-W %s' doesn't allow an argument\n"),
+ argv[0], pfound->name);
+
+ nextchar += strlen (nextchar);
+ return '?';
+ }
+ } else if ( pfound->has_arg == 1 ) {
+ if ( gnu_optind < argc )
+ gnu_optarg = argv[gnu_optind++];
+ else {
+ if ( gnu_opterr )
+ fprintf (stderr,
+ _("%s: option `%s' requires an argument\n"),
+ argv[0], argv[gnu_optind - 1]);
+ nextchar += strlen (nextchar);
+ return optstring[0] == ':' ? ':' : '?';
+ }
+ }
+ nextchar += strlen (nextchar);
+ if ( longind != NULL )
+ *longind = option_index;
+ if ( pfound->flag ) {
+ *(pfound->flag) = pfound->val;
+ return 0;
+ }
+ return pfound->val;
+ }
+ nextchar = NULL;
+ return 'W'; /* Let the application handle it. */
+ }
+ if ( temp[1] == ':' ) {
+ if ( temp[2] == ':' ) {
+ /* This is an option that accepts an argument optionally. */
+ if ( *nextchar != '\0' ) {
+ gnu_optarg = nextchar;
+ gnu_optind++;
+ } else
+ gnu_optarg = NULL;
+ nextchar = NULL;
+ } else {
+ /* This is an option that requires an argument. */
+ if ( *nextchar != '\0' ) {
+ gnu_optarg = nextchar;
+ /* If we end this ARGV-element by taking the rest as an arg,
+ we must advance to the next element now. */
+ gnu_optind++;
+ } else if ( gnu_optind == argc ) {
+ if ( gnu_opterr ) {
+ /* 1003.2 specifies the format of this message. */
+ fprintf (stderr,
+ _("%s: option requires an argument -- %c\n"),
+ argv[0], c);
+ }
+ gnu_optopt = c;
+ if ( optstring[0] == ':' )
+ c = ':';
+ else
+ c = '?';
+ } else
+ /* We already incremented `gnu_optind' once;
+ increment it again when taking next ARGV-elt as argument. */
+ gnu_optarg = argv[gnu_optind++];
+ nextchar = NULL;
+ }
+ }
+ return c;
+ }
+}
+
+int
+gnu_getopt ( int argc,
+ char *const *argv,
+ const char *optstring ) {
+ return _gnu_getopt_internal (argc, argv, optstring,
+ (const struct option *) 0,
+ (int *) 0,
+ 0);
+}
+
+#ifdef __cplusplus
+} /* end extern "C" */
+#endif
+
+
+#ifdef TEST
+
+/* Compile with -DTEST to make an executable for use in testing
+ the above definition of `gnu_getopt'. */
+
+int
+main (argc, argv)
+int argc;
+char **argv;
+{
+int c;
+int digit_optind = 0;
+
+while ( 1 ) {
+ int this_option_optind = gnu_optind ? gnu_optind : 1;
+
+ c = gnu_getopt (argc, argv, "abc:d:0123456789");
+ if ( c == -1 )
+ break;
+
+ switch ( c ) {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if ( digit_optind != 0 && digit_optind != this_option_optind )
+ fprintf ( stderr, "digits occur in two different argv-elements.\n");
+ digit_optind = this_option_optind;
+ fprintf ( stderr, "option %c\n", c);
+ break;
+
+ case 'a':
+ fprintf ( stderr, "option a\n");
+ break;
+
+ case 'b':
+ fprintf ( stderr, "option b\n");
+ break;
+
+ case 'c':
+ fprintf ( stderr, "option c with value `%s'\n", gnu_optarg);
+ break;
+
+ case '?':
+ break;
+
+ default:
+ fprintf ( stderr, "?? gnu_getopt returned character code 0%o ??\n", c);
+ }
+}
+
+if ( gnu_optind < argc ) {
+ fprintf (stderr, "non-option ARGV-elements: ");
+ while ( gnu_optind < argc )
+ fprintf ( stderr, "%s ", argv[gnu_optind++]);
+ fprintf ( stderr, "\n");
+}
+
+exit (0);
+}
+
+#endif /* TEST */
diff --git a/src/gnu_getopt_long.c b/src/gnu_getopt_long.c
new file mode 100644
index 0000000..9049feb
--- /dev/null
+++ b/src/gnu_getopt_long.c
@@ -0,0 +1,162 @@
+/* gnu_getopt_long and getopt_long_only entry points for GNU getopt.
+ Copyright (C) 1987,88,89,90,91,92,93,94,96,97 Free Software Foundation, Inc.
+
+ This file is part of the GNU C Library. Its master source is NOT part of
+ the C library, however. The master source lives in /gd/gnu/lib.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The GNU C Library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the GNU C Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+/*
+ * modified July 9, 1999 by mark gates <mgates@nlanr.net>
+ * Dec 17, 1999
+ *
+ * renamed all functions and variables by prepending "gnu_"
+ * removed/redid a bunch of stuff under the assumption we're
+ * using a modern standard C compiler.
+ * renamed file to gnu_getopt_long.c (from gnu_getopt1.c)
+ *
+ * $Id: gnu_getopt_long.c,v 1.1.1.1 2004/05/18 01:50:44 kgibbs Exp $
+ */
+
+
+
+#include "gnu_getopt.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int
+gnu_getopt_long( int argc,
+ char *const *argv,
+ const char *options,
+ const struct option *long_options,
+ int *opt_index ) {
+ return _gnu_getopt_internal (argc, argv, options, long_options, opt_index, 0);
+}
+
+/* Like gnu_getopt_long, but '-' as well as '--' can indicate a long option.
+ If an option that starts with '-' (not '--') doesn't match a long option,
+ but does match a short option, it is parsed as a short option
+ instead. */
+
+int
+gnu_getopt_long_only( int argc,
+ char *const *argv,
+ const char *options,
+ const struct option *long_options,
+ int *opt_index ) {
+ return _gnu_getopt_internal (argc, argv, options, long_options, opt_index, 1);
+}
+
+#ifdef __cplusplus
+} /* end extern "C" */
+#endif
+
+
+#ifdef TEST
+
+ #include <stdio.h>
+
+int
+main (argc, argv)
+int argc;
+char **argv;
+{
+int c;
+int digit_optind = 0;
+
+while ( 1 ) {
+ int this_option_optind = gnu_optind ? gnu_optind : 1;
+ int option_index = 0;
+ static struct option long_options[] =
+ {
+ {"add", 1, 0, 0},
+ {"append", 0, 0, 0},
+ {"delete", 1, 0, 0},
+ {"verbose", 0, 0, 0},
+ {"create", 0, 0, 0},
+ {"file", 1, 0, 0},
+ {0, 0, 0, 0}
+ };
+
+ c = gnu_getopt_long (argc, argv, "abc:d:0123456789",
+ long_options, &option_index);
+ if ( c == -1 )
+ break;
+
+ switch ( c ) {
+ case 0:
+ fprintf ( stderr, "option %s", long_options[option_index].name);
+ if ( gnu_optarg )
+ fprintf ( stderr, " with arg %s", gnu_optarg);
+ fprintf ( stderr, "\n");
+ break;
+
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if ( digit_optind != 0 && digit_optind != this_option_optind )
+ fprintf ( stderr, "digits occur in two different argv-elements.\n");
+ digit_optind = this_option_optind;
+ fprintf ( stderr, "option %c\n", c);
+ break;
+
+ case 'a':
+ fprintf ( stderr, "option a\n");
+ break;
+
+ case 'b':
+ fprintf ( stderr, "option b\n");
+ break;
+
+ case 'c':
+ fprintf ( stderr, "option c with value `%s'\n", gnu_optarg);
+ break;
+
+ case 'd':
+ fprintf ( stderr, "option d with value `%s'\n", gnu_optarg);
+ break;
+
+ case '?':
+ break;
+
+ default:
+ fprintf ( stderr, "?? gnu_getopt returned character code 0%o ??\n", c);
+ }
+}
+
+if ( gnu_optind < argc ) {
+ fprintf ( stderr, "non-option ARGV-elements: ");
+ while ( gnu_optind < argc )
+ fprintf ( stderr, "%s ", argv[gnu_optind++]);
+ fprintf ( stderr, "\n");
+}
+
+exit (0);
+}
+
+#endif /* TEST */
diff --git a/src/histogram.c b/src/histogram.c
new file mode 100644
index 0000000..3a9b82c
--- /dev/null
+++ b/src/histogram.c
@@ -0,0 +1,269 @@
+/*---------------------------------------------------------------
+ * Copyright (c) 2017
+ * Broadcom Corporation
+ * All Rights Reserved.
+ *---------------------------------------------------------------
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software 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 name of Broadcom Coporation,
+ * 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.
+ * ________________________________________________________________
+ *
+ * histograms.c
+ * Suppport for histograms
+ *
+ * by Robert J. McMahon (rjmcmahon@rjmcmahon.com, bob.mcmahon@broadcom.com)
+ * -------------------------------------------------------------------
+ */
+#include "headers.h"
+#include "histogram.h"
+#ifdef HAVE_THREAD_DEBUG
+// needed for thread_debug
+#include "Thread.h"
+#endif
+struct histogram *histogram_init(unsigned int bincount, unsigned int binwidth, float offset, float units,\
+ double ci_lower, double ci_upper, unsigned int id, char *name) {
+ struct histogram *this = (struct histogram *) malloc(sizeof(struct histogram));
+ if (!this) {
+ fprintf(stderr,"Malloc failure in histogram init\n");
+ return(NULL);
+ }
+ if (!bincount)
+ bincount = 1000;
+ this->mybins = (unsigned int *) malloc(sizeof(unsigned int) * bincount);
+ if (!this->mybins) {
+ fprintf(stderr,"Malloc failure in histogram init b\n");
+ free(this);
+ return(NULL);
+ }
+ this->myname = (char *) malloc(sizeof(strlen(name)));
+ if (!this->myname) {
+ fprintf(stderr,"Malloc failure in histogram init n\n");
+ free(this->mybins);
+ free(this);
+ return(NULL);
+ }
+ this->outbuf = (char *) malloc(120 + (32*bincount) + strlen(name));
+ if (!this->outbuf) {
+ fprintf(stderr,"Malloc failure in histogram init o\n");
+ free(this->myname);
+ free(this->mybins);
+ free(this);
+ return(NULL);
+ }
+ memset(this->mybins, 0, bincount * sizeof(unsigned int));
+ strcpy(this->myname, name);
+ this->id = id;
+ this->bincount = bincount;
+ this->binwidth = binwidth;
+ this->populationcnt = 0;
+ this->offset=offset;
+ this->units=units;
+ this->cntloweroutofbounds=0;
+ this->cntupperoutofbounds=0;
+ this->ci_lower = ci_lower;
+ this->ci_upper = ci_upper;
+ this->prev = NULL;
+ this->maxbin = -1;
+ this->fmaxbin = -1;
+ this->maxts.tv_sec = 0;
+ this->maxts.tv_usec = 0;
+ this->fmaxts.tv_sec = 0;
+ this->fmaxts.tv_usec = 0;
+#ifdef HAVE_THREAD_DEBUG
+ thread_debug("histo create %p", (void *) this);
+#endif
+ return this;
+}
+
+void histogram_delete(struct histogram *h) {
+#ifdef HAVE_THREAD_DEBUG
+ thread_debug("histo delete %p", (void *) h);
+#endif
+ if (h) {
+ if (h->prev)
+ histogram_delete(h->prev);
+ if (h->mybins)
+ free(h->mybins);
+ if (h->myname)
+ free(h->myname);
+ free(h);
+ }
+}
+
+// value is units seconds
+int histogram_insert(struct histogram *h, float value, struct timeval *ts) {
+ int bin;
+ // calculate the bin, convert the value units from seconds to units of interest
+ bin = (int) (h->units * (value - h->offset) / h->binwidth);
+ h->populationcnt++;
+ if (ts && (value > h->maxval)) {
+ h->maxbin = bin;
+ h->maxval = value;
+ h->maxts.tv_sec = ts->tv_sec;
+ h->maxts.tv_usec = ts->tv_usec;
+ // printf("imax=%ld.%ld %f\n",h->maxts.tv_sec, h->maxts.tv_usec, value);
+ if (value > h->fmaxval) {
+ h->fmaxbin = bin;
+ h->fmaxval = value;
+ h->fmaxts.tv_sec = ts->tv_sec;
+ h->fmaxts.tv_usec = ts->tv_usec;
+ // printf("fmax=%ld.%ld %f\n",h->fmaxts.tv_sec, h->fmaxts.tv_usec, value);
+ }
+ }
+ if (bin < 0) {
+ h->cntloweroutofbounds++;
+ return(-1);
+ } else if (bin > (int) h->bincount) {
+ h->cntupperoutofbounds++;
+ return(-2);
+ }
+ else {
+ h->mybins[bin]++;
+ return(h->mybins[bin]);
+ }
+}
+
+void histogram_clear(struct histogram *h) {
+ memset(h->mybins, 0, (h->bincount * sizeof(unsigned int)));
+ h->populationcnt = 0;
+ h->cntloweroutofbounds=0;
+ h->cntupperoutofbounds=0;
+ h->maxbin = 0;
+ h->maxts.tv_sec = 0;
+ h->maxts.tv_usec = 0;
+ if (h->prev)
+ histogram_clear(h->prev);
+ h->prev = NULL;
+}
+
+void histogram_add(struct histogram *to, struct histogram *from) {
+ int ix;
+ assert(to != NULL);
+ assert(from != NULL);
+ if (to->bincount <= from->bincount) {
+ for (ix=0; ix < to->bincount; ix ++) {
+ to->mybins[ix] += from->mybins[ix];
+ }
+ to->populationcnt += from->populationcnt;
+ to->cntloweroutofbounds += from->cntloweroutofbounds;
+ to->cntupperoutofbounds += from->cntupperoutofbounds;
+ if (from->maxbin > to->maxbin) {
+ to->maxbin = from->maxbin;
+ }
+ if (from->maxts.tv_sec > to->maxts.tv_sec) {
+ to->maxts.tv_sec = from->maxts.tv_sec;
+ to->maxts.tv_usec = from->maxts.tv_usec;
+ } else if ((from->maxts.tv_sec == to->maxts.tv_sec) && \
+ (from->maxts.tv_usec > to->maxts.tv_usec)) {
+ to->maxts.tv_usec = from->maxts.tv_usec;
+ }
+ }
+}
+
+void histogram_print(struct histogram *h, double start, double end) {
+ if (h->final && h->prev) {
+ histogram_clear(h->prev);
+ }
+ if (!h->prev) {
+ h->prev = histogram_init(h->bincount, h->binwidth, h->offset, h->units, h->ci_lower, h->ci_upper, h->id, h->myname);
+ }
+ int n = 0, ix, delta, lowerci, upperci, outliercnt, fence_lower, fence_upper, upper3stdev;
+ int running=0;
+ int intervalpopulation, oob_u, oob_l;
+ intervalpopulation = h->populationcnt - h->prev->populationcnt;
+ strcpy(h->outbuf, h->myname);
+ sprintf(h->outbuf, "[%3d] " IPERFTimeFrmt " sec %s%s%s bin(w=%d%s):cnt(%d)=", h->id, start, end, h->myname, (h->final ? "(f)" : ""), "-PDF:",h->binwidth, ((h->units == 1e3) ? "ms" : "us"), intervalpopulation);
+ n = strlen(h->outbuf);
+ lowerci=0;
+ upperci=0;
+ upper3stdev = 0;
+ outliercnt=0;
+ fence_lower = 0;
+ fence_upper = 0;
+ int outside3fences = 0;
+ h->prev->populationcnt = h->populationcnt;
+ oob_l = h->cntloweroutofbounds - h->prev->cntloweroutofbounds;
+ h->prev->cntloweroutofbounds = h->cntloweroutofbounds;
+ oob_u = h->cntupperoutofbounds - h->prev->cntupperoutofbounds;
+ h->prev->cntupperoutofbounds = h->cntupperoutofbounds;
+
+ for (ix = 0; ix < h->bincount; ix++) {
+ delta = h->mybins[ix] - h->prev->mybins[ix];
+ if (delta > 0) {
+ running+=delta;
+ if (!lowerci && ((float)running/intervalpopulation > h->ci_lower/100.0)) {
+ lowerci = ix+1;
+ }
+ // use 10% and 90% for inner fence post, then 3 times for outlier
+ if ((float)running/intervalpopulation < 0.1) {
+ fence_lower=ix+1;
+ }
+ if ((float)running/intervalpopulation < 0.9) {
+ fence_upper=ix+1;
+ } else if (!outside3fences) {
+ outside3fences = fence_upper + (3 * (fence_upper - fence_lower));
+ } else if (ix > outside3fences) {
+ outliercnt += delta;
+ }
+ if (!upperci && ((float)running/intervalpopulation > h->ci_upper/100.0)) {
+ upperci = ix+1;
+ }
+ if (!upper3stdev && ((float)running/intervalpopulation > 99.7/100.0)) {
+ upper3stdev = ix+1;
+ }
+ n += sprintf(h->outbuf + n,"%d:%d,", ix+1, delta);
+ h->prev->mybins[ix] = h->mybins[ix];
+ }
+ }
+ h->outbuf[strlen(h->outbuf)-1] = '\0';
+ if (!upperci)
+ upperci=h->bincount;
+ if (!upper3stdev)
+ upper3stdev=h->bincount;
+ if (h->ci_upper > 99.7)
+ fprintf(stdout, "%s (%.2f/99.7/%.2f/%%=%d/%d/%d,Outliers=%d,obl/obu=%d/%d)", \
+ h->outbuf, h->ci_lower, h->ci_upper, lowerci, upper3stdev, upperci, outliercnt, oob_l, oob_u);
+ else
+ fprintf(stdout, "%s (%.2f/%.2f/99.7%%=%d/%d/%d,Outliers=%d,obl/obu=%d/%d)", \
+ h->outbuf, h->ci_lower, h->ci_upper, lowerci, upperci, upper3stdev, outliercnt, oob_l, oob_u);
+ if (!h->final && (h->maxval > 0) && ((h->maxts.tv_sec > 0) || h->maxts.tv_usec > 0)) {
+ fprintf(stdout, " (%0.3f ms/%ld.%ld)\n", (h->maxval * 1e3), (long) h->maxts.tv_sec, (long) h->maxts.tv_usec);
+ h->maxbin = -1;
+ h->maxval = 0;
+ } else if (h->final && (h->fmaxval > 0) && ((h->maxts.tv_sec > 0) || h->maxts.tv_usec > 0)) {
+ fprintf(stdout, " (%0.3f ms/%ld.%ld)\n", (h->fmaxval * 1e3), (long) h->fmaxts.tv_sec, (long) h->fmaxts.tv_usec);
+ } else {
+ fprintf(stdout, "\n");
+ }
+}
diff --git a/src/igmp_querier.c b/src/igmp_querier.c
new file mode 100644
index 0000000..d18af5b
--- /dev/null
+++ b/src/igmp_querier.c
@@ -0,0 +1,213 @@
+/*
+ * Copyright (c) 2018
+ * Broadcom Corporation
+ * All Rights Reserved.
+ * Redistribution and use in source and binary forms, with or without modification, are permitted
+ * provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this list of conditions
+ * and the following disclaimer. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the documentation and/or other
+ * materials provided with the distribution. Neither the name of the Broadcom nor the names of
+ * contributors may be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USEn,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Author Robert J. McMahon, Broadcom LTD
+ * Date April 2016
+ *
+ * IGMP 2 querier that can be controlled remotely
+ *
+ * Author Robert J. McMahon (rmcmahon)
+ * Last modified: 06/25/2010
+ *
+ * $Copyright Open Broadcom Corporation$
+ *
+ */
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+#include <stdio.h>
+#include <signal.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <time.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <strings.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <arpa/inet.h>
+#include "headers.h"
+
+#define IGMP_HEADER_SIZE 8
+
+void sigint();
+void siguser();
+void sigalrm();
+static int querier_sent_count = 0;
+static int sendcount = 0;
+static short daemonmode = 0;
+static char mcast_queryaddr[20] = "224.0.0.1";
+static int send_igmp_allhosts_querier (char *);
+
+
+int main (int argc, char **argv) {
+ char *tvalue = NULL;
+ char *cvalue = NULL;
+ int c;
+ int queryinterval;
+ int pid;
+
+ opterr = 0;
+ while ((c = getopt (argc, argv, "c:dg:t:")) != -1) {
+ switch (c) {
+ case 'c':
+ cvalue = optarg;
+ break;
+ case 'g' :
+ strcpy(mcast_queryaddr, optarg);
+ break;
+ case 't':
+ tvalue = optarg;
+ break;
+ case 'd':
+ daemonmode = 1;
+ break;
+ case '?':
+ if (optopt == 't')
+ fprintf (stderr, "Option -%c requires an integer argument.\n", optopt);
+ else if (optopt == 'c')
+ fprintf (stderr, "Option -%c requires an integer argument.\n", optopt);
+ else if (isprint (optopt))
+ fprintf (stderr, "Unknown option `-%c'.\n", optopt);
+ else
+ fprintf (stderr,
+ "Unknown option character `\\x%x'.\n",
+ optopt);
+ default:
+ fprintf(stderr,"Usage -c <count>, -d daemon, -g <group>, -t <period in seconds>\n");
+ exit(-1);
+ }
+ }
+ if (tvalue != NULL) {
+ queryinterval = atoi(tvalue);
+ } else {
+ queryinterval = 0;
+ }
+ if (cvalue != NULL) {
+ sendcount = atoi(cvalue);
+ if (tvalue == NULL)
+ queryinterval = 0;
+ } else {
+ sendcount = 1;
+ }
+ signal(SIGINT, sigint);
+ signal(SIGUSR1, siguser);
+ signal(SIGALRM, sigalrm);
+ pid = (int) getpid();
+ if (daemonmode) {
+ if (tvalue != NULL) {
+ sendcount = 1;
+ printf("IGMP All Hosts Querier (pid=%d) started as a daemon with interval of %d seconds to %s\n", pid, queryinterval, mcast_queryaddr);
+ } else {
+ sendcount = 0;
+ printf("IGMP All Hosts Querier (pid=%d) to %s started as a daemon only\n", pid, mcast_queryaddr);
+ }
+ } else {
+ printf("IGMP All Hosts Querier (pid=%d) sending %d reports with interval of %d seconds\n", pid, sendcount, queryinterval);
+ }
+ fflush(stdout);
+ while (daemonmode || sendcount) {
+ if (sendcount-- > 0) {
+ send_igmp_allhosts_querier(mcast_queryaddr);
+ }
+ if (queryinterval) {
+ alarm(queryinterval);
+ pause();
+ }
+ if (daemonmode && sendcount <= 0)
+ pause();
+ }
+}
+
+void sigint (void) {
+ exit(0);
+}
+
+void siguser (void) {
+ if (!send_igmp_allhosts_querier(mcast_queryaddr)) {
+ exit (-1);
+ }
+}
+
+void sigalrm (void) {
+ if (daemonmode)
+ sendcount++;
+}
+
+static int send_igmp_allhosts_querier (char *mcast_queryaddr) {
+ int sid=0;
+ int rc;
+ char buf[IGMP_HEADER_SIZE];
+ unsigned int ttl=1;
+ char type = 17;
+ char maxresptime = 1;
+ // unsigned int groupaddr = 0;
+ char checksum_upper = 0xee;
+ char checksum_lower = 0xfe;
+ struct sockaddr_in msock;
+ const time_t timer = time(NULL);
+
+ bzero (&buf, sizeof(buf));
+ /*
+ * Note: with byte writes shouldn't need to worry about network/host
+ * byte ordering, though should double check on non intel system
+ */
+ buf[0] = type;
+ buf[1] = maxresptime;
+ buf[2] = checksum_upper;
+ buf[3] = checksum_lower;
+
+ bzero (&msock, sizeof(msock));
+ msock.sin_family = AF_INET;
+
+ inet_pton(AF_INET, mcast_queryaddr, &msock.sin_addr);
+ setuid(geteuid());
+ sid = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP);
+ if (sid != -1) {
+ rc = setsockopt(sid, IPPROTO_IP, IP_TTL, (char *) &ttl, sizeof(ttl));
+ if (rc != -1) {
+ rc = sendto(sid, &buf, IGMP_HEADER_SIZE, 0, (const struct sockaddr *) &msock, sizeof(msock));
+ if (rc == -1) {
+ fprintf(stderr, "IGMP Query sendto error = %s\n", strerror(errno));
+ }
+ } else {
+ fprintf(stderr, "IGMP Query setsockopt error = %s\n", strerror(errno));
+ }
+ } else {
+ fprintf(stderr, "IGMP Query, socket error = %s\n", strerror(errno));
+ }
+ if (sid > 0) {
+ close(sid);
+ rc=1;
+ } else {
+ rc = 0;
+ }
+ setuid(getuid());
+ if (rc) {
+ printf("Sent IGMP all hosts querier to %s (count = %d) at %s",mcast_queryaddr, ++querier_sent_count, ctime(&timer));
+ fflush(stdout);
+ }
+ return rc;
+}
diff --git a/src/iperf_formattime.c b/src/iperf_formattime.c
new file mode 100644
index 0000000..df65650
--- /dev/null
+++ b/src/iperf_formattime.c
@@ -0,0 +1,100 @@
+/*---------------------------------------------------------------
+ * Copyright (c) 2023
+ * Broadcom Corporation
+ * All Rights Reserved.
+ *---------------------------------------------------------------
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software 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 name of Broadcom Coporation,
+ * 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.
+ * ________________________________________________________________
+ *
+ * iperf_formattime
+ * wrapper around strftime.c for iperf supported formatting
+ *
+ * by Robert J. McMahon (rjmcmahon@rjmcmahon.com, bob.mcmahon@broadcom.com)
+ * -------------------------------------------------------------------
+ */
+#include "headers.h"
+#include "util.h"
+#include "iperf_formattime.h"
+
+inline void iperf_formattime (char *timestr, int buflen, struct timeval timestamp, bool prec_ms, bool utc_time, enum TimeFormatType ftype) {
+ struct tm ts ;
+ ts = (utc_time ? *gmtime(&timestamp.tv_sec) : *localtime(&timestamp.tv_sec));
+ switch (ftype) {
+ case YearThruSec:
+ strftime(timestr, buflen, "%Y-%m-%d %H:%M:%S", &ts);
+ if (prec_ms) {
+ int currlen = strlen(timestr);
+ if (currlen > 5) {
+ snprintf((timestr + currlen), 5, ".%.3d", (int) (timestamp.tv_usec/1000));
+ }
+ }
+ break;
+ case YearThruSecTZ:
+ strftime(timestr, buflen, "%Y-%m-%d %H:%M:%S", &ts);
+ int currlen = strlen(timestr);
+ if (prec_ms) {
+ if (currlen > 5) {
+ snprintf((timestr + currlen), 5, ".%.3d", (int) (timestamp.tv_usec/1000));
+ currlen = strlen(timestr);
+ }
+ }
+ if ((buflen - currlen) > 5) {
+ strftime((timestr + currlen), (buflen - currlen), " (%Z)", &ts);
+ }
+ break;
+ case CSV:
+ strftime(timestr, buflen, "%Y%m%d%H%M%S", &ts);
+ if (prec_ms) {
+ int currlen = strlen(timestr);
+ if (currlen > 5) {
+ snprintf((timestr + currlen), 5, ".%.3d", (int) (timestamp.tv_usec/1000));
+ }
+ }
+ break;
+ case CSVTZ:
+ strftime(timestr, buflen, "%z:%Y%m%d%H%M%S", &ts);
+ if (prec_ms) {
+ int currlen = strlen(timestr);
+ if (currlen > 5) {
+ snprintf((timestr + currlen), 5, ".%.3d", (int) (timestamp.tv_usec/1000));
+ }
+ }
+ break;
+ default:
+ FAIL_exit(1, "iperf_formattime program error");
+ }
+ timestr[buflen - 1] = '\0'; // make sure string is null terminated
+}
diff --git a/src/isochronous.cpp b/src/isochronous.cpp
new file mode 100644
index 0000000..573f05a
--- /dev/null
+++ b/src/isochronous.cpp
@@ -0,0 +1,275 @@
+/*---------------------------------------------------------------
+ * Copyright (c) 2017-2021
+ * Broadcom Corporation
+ * All Rights Reserved.
+ *---------------------------------------------------------------
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software 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 name of Broadcom Coporation,
+ * 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.
+ * ________________________________________________________________
+ *
+ * isochronous.c
+ * Suppport for isochonronous traffic testing
+ *
+ * by Robert J. McMahon (rjmcmahon@rjmcmahon.com, bob.mcmahon@broadcom.com)
+ * -------------------------------------------------------------------
+ */
+#include "headers.h"
+#include "Timestamp.hpp"
+#include "isochronous.hpp"
+#include "delay.h"
+
+#define BILLION 1000000000
+
+using namespace Isochronous;
+
+FrameCounter::FrameCounter (double value, const Timestamp& start) : frequency(value) {
+ period = static_cast<unsigned int>(1000000 / frequency);
+ startTime = start;
+ nextslotTime=start;
+ lastcounter = 0;
+ slot_counter = 0;
+ slip = 0;
+}
+FrameCounter::FrameCounter (double value) : frequency(value) {
+#ifdef WIN32
+ /* Create timer */
+ my_timer = CreateWaitableTimer(NULL, TRUE, NULL);
+ if (!SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL))
+ WARN_errno(1, "SetThreadPriority");
+#endif
+ period = static_cast<unsigned int>(1000000 / frequency); // unit us
+ lastcounter = 0;
+ slot_counter = 0;
+ slip = 0;
+}
+
+
+FrameCounter::~FrameCounter () {
+#ifdef WIN32
+ /* Clean resources */
+ if (my_timer)
+ CloseHandle(my_timer);
+#endif
+}
+
+#ifdef WIN32
+/* Windows sleep in 100ns units returns 0 on success as does clock_nanosleep*/
+int FrameCounter::mySetWaitableTimer (long delay_time) {
+ int rc = -1;
+ if (!my_timer) {
+ if ((my_timer = CreateWaitableTimer(NULL, TRUE, NULL))) {
+ /* Set timer properties */
+ delay.QuadPart = -delay_time;
+ } else {
+ WARN_errno(1, "CreateWaitable");
+ my_timer = NULL;
+ }
+ }
+ if (my_timer) {
+ /* Set timer properties */
+ /* negative values are relative, positive absolute UTC time */
+ delay.QuadPart = -delay_time;
+ if(!SetWaitableTimer(my_timer, &delay, 0, NULL, NULL, FALSE)) {
+ WARN_errno(1, "SetWaitableTimer");
+ CloseHandle(my_timer);
+ my_timer = NULL;
+ } else {
+ // Wait for timer
+ if (WaitForSingleObject(my_timer, INFINITE)) {
+ WARN_errno(1, "WaitForSingleObject");
+ } else {
+ rc = 0;
+ }
+ }
+ }
+ return rc;
+}
+#endif
+
+#if HAVE_CLOCK_NANOSLEEP
+unsigned int FrameCounter::wait_tick (long *sched_err) {
+ Timestamp now;
+ int rc = true;
+ if (!slot_counter) {
+ slot_counter = 1;
+ now.setnow();
+ nextslotTime = now;
+ } else {
+ while (!now.before(nextslotTime)) {
+ now.setnow();
+ nextslotTime.add(period);
+// printf("***** next slot %ld.%ld\n",nextslotTime.getSecs(), nextslotTime.getUsecs());
+ slot_counter++;
+ }
+ if (lastcounter && ((slot_counter - lastcounter) > 1)) {
+ slip++;
+ }
+ }
+ #ifndef WIN32
+ timespec txtime_ts;
+ txtime_ts.tv_sec = nextslotTime.getSecs();
+ txtime_ts.tv_nsec = nextslotTime.getUsecs() * 1000;
+ rc = clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &txtime_ts, NULL);
+ #else
+ long duration = nextslotTime.subUsec(now);
+ rc = mySetWaitableTimer(10 * duration); // convert us to 100 ns
+ //int rc = clock_nanosleep(0, TIMER_ABSTIME, &txtime_ts, NULL);
+ #endif
+ if (sched_err) {
+ // delay_loop(2020);
+ Timestamp actual;
+ *sched_err = actual.subUsec(nextslotTime);
+// printf("**** sched err %ld\n", *sched_err);
+ if (*sched_err < 0) {
+ *sched_err = -(*sched_err); // err is an absolute value
+ // Per windows docs, this timer can go off early per:
+ // APIs that deal with timers use various different hardware clocks. These clocks may have resolutions
+ // significantly different from what you expect: some may be measured in milliseconds (for those that
+ // use an RTC-based timer chip), to those measured in nanoseconds (for those that use ACPI or TSC counters).
+ // You can change the resolution of your API with a call to the timeBeginPeriod and timeEndPeriod functions.
+ // How precise you can change the resolution depends on which hardware clock the particular API uses.
+ // For more information, check your hardware documentation.
+ //
+ // I noticed the timer going off up to 8 ms early on a Windows 11 cross compile - yikes.
+ // Do a WAR hack here to add delay if & when that occurs
+#ifdef WIN32
+ if (*sched_err > 1000) {
+ delay_loop(*sched_err);
+ }
+#endif
+ }
+ }
+ WARN_errno((rc!=0), "wait_tick failed");
+ #ifdef HAVE_THREAD_DEBUG
+ // thread_debug("Client tick occurred per %ld.%ld", txtime_ts.tv_sec, txtime_ts.tv_nsec / 1000);
+ #endif
+ lastcounter = slot_counter;
+ return(slot_counter);
+}
+#else
+unsigned int FrameCounter::wait_tick (long *sched_err) {
+ long remaining;
+ unsigned int framecounter;
+
+ if (!lastcounter) {
+ reset();
+ framecounter = 1;
+ } else {
+ framecounter = get(&remaining);
+ if ((framecounter - lastcounter) > 1)
+ slip++;
+// delay_loop(remaining);
+ remaining *= 1000;
+ struct timespec tv0={0,0}, tv1;
+ tv0.tv_sec = (remaining / BILLION);
+ tv0.tv_nsec += (remaining % BILLION);
+ if (tv0.tv_nsec >= BILLION) {
+ tv0.tv_sec++;
+ tv0.tv_nsec -= BILLION;
+ }
+ Timestamp slotstart = startTime;
+ slot_counter = get();
+ // period unit is in microseconds, convert to seconds
+ slotstart.add(slot_counter * period * 1e-6);
+ int rc = nanosleep(&tv0, &tv1);
+ if (sched_err) {
+ Timestamp actual;
+ *sched_err = actual.subUsec(slotstart);
+// printf("**** slot %ld.%ld actual %ld.%ld %ld\n", slotstart.getSecs(), slotstart.getUsecs(), actual.getSecs(), actual.getUsecs(), *sched_err);
+ }
+ WARN_errno((rc != 0), "nanosleep wait_tick"); ;
+// printf("****** rc = %d, remain %ld.%ld\n", rc, tv1.tv_sec, tv1.tv_nsec);
+ framecounter ++;
+ }
+ lastcounter = framecounter;
+ return(framecounter);
+}
+#endif
+inline unsigned int FrameCounter::get () const {
+ Timestamp now;
+ return slot_counter + 1;
+}
+
+inline unsigned int FrameCounter::get (const Timestamp& slot) const {
+ return(slot_counter + 1); // Frame counter for packets starts at 1
+}
+
+inline unsigned int FrameCounter::get (long *ticks_remaining) {
+ assert(ticks_remaining != NULL);
+ Timestamp sampleTime; // Constructor will initialize timestamp to now
+ long usecs = -startTime.subUsec(sampleTime);
+ unsigned int counter = static_cast<unsigned int>(usecs / period) + 1;
+ // figure out how many usecs before the next frame counter tick
+ // the caller can use this to delay until the next tick
+ *ticks_remaining = (counter * period) - usecs;
+ return(counter + 1); // Frame counter for packets starts at 1
+}
+
+inline Timestamp FrameCounter::next_slot () {
+ Timestamp next = startTime;
+ slot_counter = get();
+ // period unit is in microseconds, convert to seconds
+ next.add(slot_counter * (period / 1e6));
+ return next;
+}
+
+unsigned int FrameCounter::period_us () {
+ return(period);
+}
+
+void FrameCounter::reset () {
+ period = (1000000 / frequency);
+ startTime.setnow();
+}
+
+unsigned int FrameCounter::wait_sync (long sec, long usec) {
+ long remaining;
+ unsigned int framecounter;
+ startTime.set(sec, usec);
+ framecounter = get(&remaining);
+ delay_loop(remaining);
+ reset();
+ framecounter = 1;
+ lastcounter = 1;
+ return(framecounter);
+}
+
+long FrameCounter::getSecs () {
+ return startTime.getSecs();
+}
+
+long FrameCounter::getUsecs () {
+ return startTime.getUsecs();
+}
diff --git a/src/main.cpp b/src/main.cpp
new file mode 100644
index 0000000..7038faa
--- /dev/null
+++ b/src/main.cpp
@@ -0,0 +1,479 @@
+/*---------------------------------------------------------------
+ * Copyright (c) 1999,2000,2001,2002,2003
+ * The Board of Trustees of the University of Illinois
+ * All Rights Reserved.
+ *---------------------------------------------------------------
+ * 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.
+ * ________________________________________________________________
+ * National Laboratory for Applied Network Research
+ * National Center for Supercomputing Applications
+ * University of Illinois at Urbana-Champaign
+ * http://www.ncsa.uiuc.edu
+ * ________________________________________________________________
+ * main.cpp
+ * by Mark Gates <mgates@nlanr.net>
+ * & Ajay Tirumala <tirumala@ncsa.uiuc.edu>
+ * -------------------------------------------------------------------
+ * main does initialization and creates the various objects that will
+ * actually run the iperf program, then waits in the Joinall().
+ * -------------------------------------------------------------------
+ * headers
+ * uses
+ * <stdlib.h>
+ * <string.h>
+ *
+ * <signal.h>
+ * ------------------------------------------------------------------- */
+
+#define HEADERS()
+
+#include "headers.h"
+#include "Settings.hpp"
+#include "PerfSocket.hpp"
+#include "Locale.h"
+#include "Condition.h"
+#include "Timestamp.hpp"
+#include "Listener.hpp"
+#include "active_hosts.h"
+#include "util.h"
+#include "Reporter.h"
+#include "payloads.h"
+
+#ifdef WIN32
+#include "service.h"
+#endif
+
+/* -------------------------------------------------------------------
+ * prototypes
+ * ------------------------------------------------------------------- */
+// Function called at exit to clean up as much as possible
+void cleanup();
+// signal handlers
+static void Sig_Interupt(int inSigno);
+
+/* -------------------------------------------------------------------
+ * global variables
+ * ------------------------------------------------------------------- */
+extern "C" {
+ // Global flag to signal a user interrupt
+ int sInterupted = 0;
+ // Global ID that we increment to be used
+ // as identifier for SUM reports
+ int groupID = 0;
+ // Mutex to protect access to the above ID
+ Mutex transferid_mutex;
+ // Condition used to signal the reporter thread
+ // when a packet ring is full. Shouldn't really
+ // be needed but is "belts and suspeners"
+ struct Condition ReportCond;
+ // Initialize reporter thread mutex
+ struct AwaitMutex reporter_state;
+ struct AwaitMutex threads_start;
+ struct BarrierMutex transmits_start;
+}
+
+
+// global variables only accessed within this file
+
+// Thread that received the SIGTERM or SIGINT signal
+// Used to ensure that if multiple threads receive the
+// signal we do not prematurely exit
+nthread_t sThread;
+static thread_Settings* ext_gSettings;
+// The main thread uses this function to wait
+// for all other threads to complete
+void waitUntilQuit();
+
+/* -------------------------------------------------------------------
+ * main()
+ * Entry point into Iperf
+ *
+ * sets up signal handlers
+ * initialize global locks and conditions
+ * parses settings from environment and command line
+ * starts up server or client thread
+ * waits for all threads to complete
+ * ------------------------------------------------------------------- */
+int main(int argc, char **argv) {
+
+ // Set SIGTERM and SIGINT to call our user interrupt function
+ my_signal(SIGTERM, Sig_Interupt);
+ my_signal(SIGINT, Sig_Interupt);
+#ifndef WIN32
+ my_signal(SIGALRM, Sig_Interupt);
+ // Ignore broken pipes
+ signal(SIGPIPE,SIG_IGN);
+#else
+ // Start winsock
+ WSADATA wsaData;
+ int rc = WSAStartup(0x202, &wsaData);
+ WARN_errno(rc == SOCKET_ERROR, "WSAStartup");
+ if (rc == SOCKET_ERROR)
+ return 0;
+ // Tell windows we want to handle our own signals
+ SetConsoleCtrlHandler(sig_dispatcher, true);
+#endif
+
+ // Initialize global mutexes and conditions
+ Iperf_initialize_active_table();
+ Condition_Initialize (&ReportCond);
+
+#ifdef HAVE_THREAD_DEBUG
+ Mutex_Initialize(&packetringdebug_mutex);
+ Mutex_Initialize(&thread_debug_mutex);
+#endif
+ Mutex_Initialize(&transferid_mutex);
+
+ // Initialize reporter thread mutex
+ reporter_state.ready = 0;
+ threads_start.ready = 0;
+ transmits_start.count = 0;
+ Condition_Initialize(&reporter_state.await);
+ Condition_Initialize(&threads_start.await);
+ Condition_Initialize(&transmits_start.await);
+
+ // Initialize the thread subsystem
+ thread_init();
+
+ // Initialize the interrupt handling thread to 0
+ sThread = thread_zeroid();
+
+ // perform any cleanup when quitting Iperf
+ atexit(cleanup);
+
+ // Allocate the "global" settings
+ ext_gSettings = new thread_Settings;
+ // Default reporting mode here to avoid unitialized warnings
+ // this won't be the actual mode
+ ThreadMode ReporterThreadMode = kMode_Reporter;
+ // Initialize settings to defaults
+ Settings_Initialize(ext_gSettings);
+ // read settings from environment variables
+ Settings_ParseEnvironment(ext_gSettings);
+ // read settings from command-line parameters
+ Settings_ParseCommandLine(argc, argv, ext_gSettings);
+
+ // Check for either having specified client or server
+ if ((ext_gSettings->mThreadMode != kMode_Client) && (ext_gSettings->mThreadMode != kMode_Listener)) {
+ // neither server nor client mode was specified
+ // print usage and exit
+
+#ifdef WIN32
+ // In Win32 we also attempt to start a previously defined service
+ // Starting in 2.0 to restart a previously defined service
+ // you must call iperf with "iperf -D" or using the environment variable
+ SERVICE_TABLE_ENTRY dispatchTable[] =
+ {
+ { (LPSTR)TEXT(SZSERVICENAME), (LPSERVICE_MAIN_FUNCTION)service_main},
+ { NULL, NULL}
+ };
+
+ // starting the service by SCM, there is no arguments will be passed in.
+ // the arguments will pass into Service_Main entry.
+ if (!StartServiceCtrlDispatcher(dispatchTable))
+ // If the service failed to start then print usage
+#endif
+ fprintf(stderr, usage_short, argv[0], argv[0]);
+ return 0;
+ }
+
+ if (!isSTDOUT(ext_gSettings)) {
+#ifdef HAVE_FREOPEN
+ FILE *fd;
+ fprintf(stdout, "Output from stdout (but not from stderr) will be redirected to file %s\n", ext_gSettings->mOutputFileName);
+ fflush(stdout);
+ fd = freopen(ext_gSettings->mOutputFileName, "w", stdout);
+ FAIL_errno(fd == NULL, "freopen stdout\n", ext_gSettings);
+#else
+ fprintf(stderr, "Output to file not supported\n");
+#endif
+
+ }
+
+ int mbuflen = (ext_gSettings->mBufLen > MINMBUFALLOCSIZE) ? ext_gSettings->mBufLen : MINMBUFALLOCSIZE;
+#if (((HAVE_TUNTAP_TUN) || (HAVE_TUNTAP_TAP)) && (AF_PACKET))
+ mbuflen += TAPBYTESSLOP;
+#endif
+ ext_gSettings->mBuf = new char[mbuflen];
+ memset(ext_gSettings->mBuf, 0, mbuflen);
+
+
+ unsetReport(ext_gSettings);
+ switch (ext_gSettings->mThreadMode) {
+ case kMode_Client :
+ if (isDaemon(ext_gSettings)) {
+ fprintf(stderr, "Iperf client cannot be run as a daemon\n");
+ return 0;
+ }
+ // initialize client(s)
+ transmits_start.count = ext_gSettings->mThreads;
+ ext_gSettings->connects_done = &transmits_start;
+ client_init(ext_gSettings);
+ ReporterThreadMode = kMode_ReporterClient;
+ break;
+ case kMode_Listener :
+#ifdef WIN32
+ // Remove the Windows service if requested
+ if (isRemoveService(ext_gSettings)) {
+ // remove the service
+ if (CmdRemoveService()) {
+ fprintf(stderr, "IPerf Service is removed.\n");
+ }
+ }
+ if (isDaemon(ext_gSettings)) {
+ CmdInstallService(argc, argv);
+ } else if (isRemoveService(ext_gSettings)) {
+ return 0;
+ }
+#else // *nix system
+ if (isDaemon(ext_gSettings)) {
+ fprintf(stderr, "Running Iperf Server as a daemon\n");
+ // Start the server as a daemon
+ // redirect stdin, stdout and sterr to /dev/null (see dameon and no close flag)
+ if (daemon(1, 0) < 0) {
+ perror("daemon");
+ }
+ }
+#endif
+ // Start up any parallel listener threads
+ if (ext_gSettings->mPortLast) {
+ listeners_init(ext_gSettings);
+ }
+ break;
+ default :
+ fprintf(stderr, "unknown mode");
+ break;
+ }
+#ifdef HAVE_THREAD
+ // Last step is to initialize the reporter then start all threads
+ {
+ thread_Settings *into = NULL;
+ // Create the settings structure for the reporter thread
+ Settings_Copy(ext_gSettings, &into, DEEP_COPY);
+ into->mThreadMode = ReporterThreadMode;
+ into->mSumReport = NULL;
+ into->mFullDuplexReport = NULL;
+ // Have the reporter launch the client or listener
+ into->runNow = ext_gSettings;
+ // Start all the threads that are ready to go
+ thread_start_all(into);
+ Condition_Lock(threads_start.await);
+ threads_start.ready = 1;
+ Condition_Signal(&threads_start.await);
+ Condition_Unlock(threads_start.await);
+ }
+#else
+ // No need to make a reporter thread because we don't have threads
+ thread_start(ext_gSettings);
+#endif
+ // wait for other (client, server) threads to complete
+ thread_joinall();
+ // all done!
+ return 0;
+} // end main
+
+/* -------------------------------------------------------------------
+ * Signal handler sets the sInterupted flag, so the object can
+ * respond appropriately.. [static]
+ * ------------------------------------------------------------------- */
+
+void Sig_Interupt (int inSigno) {
+#ifdef HAVE_THREAD
+ // We try to not allow a single interrupt handled by multiple threads
+ // to completely kill the app so we save off the first thread ID
+ // then that is the only thread that can supply the next interrupt
+ if ((inSigno == SIGINT) && thread_equalid(sThread, thread_zeroid())) {
+ sThread = thread_getid();
+ } else if (thread_equalid(sThread, thread_getid())) {
+ sig_exit(inSigno);
+ }
+ // global variable used by threads to see if they were interrupted
+ sInterupted = inSigno;
+
+ // Note: ignore alarms per setitimer
+#if HAVE_DECL_SIGALRM
+ if (inSigno != SIGALRM)
+#endif
+ // with threads, stop waiting for non-terminating threads
+ // (ie Listener Thread)
+ thread_release_nonterm(inSigno);
+
+#else
+ // without threads, just exit quietly, same as sig_exit()
+ sig_exit(inSigno);
+#endif
+}
+
+/* -------------------------------------------------------------------
+ * Any necesary cleanup before Iperf quits. Called at program exit,
+ * either by exit() or terminating main().
+ * ------------------------------------------------------------------- */
+
+void cleanup () {
+#ifdef WIN32
+ // Shutdown Winsock
+ WSACleanup();
+#endif
+ // clean up the list of active clients
+ Iperf_destroy_active_table();
+ // done actions
+ // Destroy global mutexes and conditions
+
+ Condition_Destroy (&ReportCond);
+ Condition_Destroy(&reporter_state.await);
+ Condition_Destroy(&threads_start.await);
+ Condition_Destroy(&transmits_start.await);
+#ifdef HAVE_THREAD_DEBUG
+ Mutex_Destroy(&packetringdebug_mutex);
+ Mutex_Destroy(&thread_debug_mutex);
+#endif
+ Mutex_Destroy(&transferid_mutex);
+ // shutdown the thread subsystem
+ thread_destroy();
+} // end cleanup
+
+#ifdef WIN32
+/*--------------------------------------------------------------------
+ * ServiceStart
+ *
+ * each time starting the service, this is the entry point of the service.
+ * Start the service, certainly it is on server-mode
+ *
+ *-------------------------------------------------------------------- */
+VOID ServiceStart (DWORD dwArgc, LPTSTR *lpszArgv) {
+ thread_Settings* ext_gSettings;
+
+ // report the status to the service control manager.
+ //
+ if (!ReportStatusToSCMgr(
+ SERVICE_START_PENDING, // service state
+ NO_ERROR, // exit code
+ 3000)) // wait hint
+ goto clean;
+
+ ext_gSettings = new thread_Settings;
+
+ // Initialize settings to defaults
+ Settings_Initialize(ext_gSettings);
+ // read settings from environment variables
+ Settings_ParseEnvironment(ext_gSettings);
+ // read settings from command-line parameters
+ Settings_ParseCommandLine(dwArgc, lpszArgv, ext_gSettings);
+
+ // Arguments will be lost when the service is started by SCM, but
+ // we need to be at least a listener
+ ext_gSettings->mThreadMode = kMode_Listener;
+
+ // report the status to the service control manager.
+ //
+ if (!ReportStatusToSCMgr(
+ SERVICE_START_PENDING, // service state
+ NO_ERROR, // exit code
+ 3000)) // wait hint
+ goto clean;
+
+ // if needed, redirect the output into a specified file
+ if (!isSTDOUT(ext_gSettings)) {
+ redirect(ext_gSettings->mOutputFileName);
+ }
+
+ // report the status to the service control manager.
+ //
+ if (!ReportStatusToSCMgr(
+ SERVICE_START_PENDING, // service state
+ NO_ERROR, // exit code
+ 3000)) // wait hint
+ goto clean;
+
+ // initialize client(s)
+ if (ext_gSettings->mThreadMode == kMode_Client) {
+ client_init(ext_gSettings);
+ }
+
+ // start up the reporter and client(s) or listener
+ {
+ thread_Settings *into = NULL;
+#ifdef HAVE_THREAD
+ Settings_Copy(ext_gSettings, &into, DEEP_COPY);
+ into->mThreadMode = kMode_Reporter;
+ into->runNow = ext_gSettings;
+#else
+ into = ext_gSettings;
+#endif
+ thread_start_all(into);
+ }
+
+ // report the status to the service control manager.
+ //
+ if (!ReportStatusToSCMgr(
+ SERVICE_RUNNING, // service state
+ NO_ERROR, // exit code
+ 0)) // wait hint
+ goto clean;
+
+ clean:
+ // wait for other (client, server) threads to complete
+ thread_joinall();
+}
+
+//
+// FUNCTION: ServiceStop
+//
+// PURPOSE: Stops the service
+//
+// PARAMETERS:
+// none
+//
+// RETURN VALUE:
+// none
+//
+// COMMENTS:
+// If a ServiceStop procedure is going to
+// take longer than 3 seconds to execute,
+// it should spawn a thread to execute the
+// stop code, and return. Otherwise, the
+// ServiceControlManager will believe that
+// the service has stopped responding.
+//
+VOID ServiceStop() {
+#ifdef HAVE_THREAD
+ Sig_Interupt(1);
+#else
+ sig_exit(1);
+#endif
+}
+
+#endif
diff --git a/src/packet_ring.c b/src/packet_ring.c
new file mode 100644
index 0000000..93e7578
--- /dev/null
+++ b/src/packet_ring.c
@@ -0,0 +1,219 @@
+/*---------------------------------------------------------------
+ * Copyright (c) 2019
+ * Broadcom Corporation
+ * All Rights Reserved.
+ *---------------------------------------------------------------
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software 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 name of Broadcom Coporation,
+ * 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.
+ * ________________________________________________________________
+ *
+ * Suppport for packet rings between threads
+ *
+ * by Robert J. McMahon (rjmcmahon@rjmcmahon.com, bob.mcmahon@broadcom.com)
+ * -------------------------------------------------------------------
+ */
+#include "headers.h"
+#include "packet_ring.h"
+#include "Condition.h"
+#include "Thread.h"
+
+#ifdef HAVE_THREAD_DEBUG
+#include "Mutex.h"
+static int totalpacketringcount = 0;
+Mutex packetringdebug_mutex;
+#endif
+
+struct PacketRing * packetring_init (int count, struct Condition *awake_consumer, struct Condition *awake_producer) {
+ assert(awake_consumer != NULL);
+ struct PacketRing *pr = NULL;
+ if ((pr = (struct PacketRing *) calloc(1, sizeof(struct PacketRing)))) {
+ pr->bytes = sizeof(struct PacketRing);
+ pr->data = (struct ReportStruct *) calloc(count, sizeof(struct ReportStruct));
+ pr->bytes += count * sizeof(struct ReportStruct);
+ }
+ if (!pr || !pr->data) {
+ fprintf(stderr, "ERROR: no memory for packet ring of size %d count, try to reduce with option --NUM_REPORT_STRUCTS\n", count);
+ exit(1);
+ }
+ pr->producer = 0;
+ pr->consumer = 0;
+ pr->maxcount = count;
+ pr->awake_producer = awake_producer;
+ pr->awake_consumer = awake_consumer;
+ if (!awake_producer)
+ pr->mutex_enable=0;
+ else
+ pr->mutex_enable=1;
+ pr->consumerdone = 0;
+ pr->awaitcounter = 0;
+#ifdef HAVE_THREAD_DEBUG
+ Mutex_Lock(&packetringdebug_mutex);
+ totalpacketringcount++;
+ thread_debug("Init %d element packet ring=%p consumer=%p producer=%p total rings=%d enable=%d", count, \
+ (void *)pr, (void *) pr->awake_consumer, (void *) pr->awake_producer, totalpacketringcount, pr->mutex_enable);
+ Mutex_Unlock(&packetringdebug_mutex);
+#endif
+ return (pr);
+}
+
+inline void packetring_enqueue (struct PacketRing *pr, struct ReportStruct *metapacket) {
+ while (((pr->producer == pr->maxcount) && (pr->consumer == 0)) || \
+ ((pr->producer + 1) == pr->consumer)) {
+ // Signal the consumer thread to process a full queue
+ if (pr->mutex_enable) {
+ assert(pr->awake_consumer != NULL);
+ Condition_Signal(pr->awake_consumer);
+ // Wait for the consumer to create some queue space
+ assert(pr->awake_producer != NULL);
+ Condition_Lock((*(pr->awake_producer)));
+ pr->awaitcounter++;
+#ifdef HAVE_THREAD_DEBUG_PERF
+ {
+ struct timeval now;
+ static struct timeval prev={0, 0};
+ gettimeofday( &now, NULL );
+ if (!prev.tv_sec || (TimeDifference(now, prev) > 1.0)) {
+ prev = now;
+ thread_debug( "Not good, traffic's packet ring %p stalled per %p", (void *)pr, (void *)&pr->awake_producer);
+ }
+ }
+#endif
+ Condition_TimedWait(pr->awake_producer, 1);
+ Condition_Unlock((*(pr->awake_producer)));
+ }
+ }
+ int writeindex;
+ if ((pr->producer + 1) == pr->maxcount)
+ writeindex = 0;
+ else
+ writeindex = (pr->producer + 1);
+
+ /* Next two lines must be maintained as is */
+ memcpy((pr->data + writeindex), metapacket, sizeof(struct ReportStruct));
+ pr->producer = writeindex;
+}
+
+inline struct ReportStruct *packetring_dequeue (struct PacketRing *pr) {
+ struct ReportStruct *packet = NULL;
+ if (pr->producer == pr->consumer)
+ return NULL;
+
+ int readindex;
+ if ((pr->consumer + 1) == pr->maxcount)
+ readindex = 0;
+ else
+ readindex = (pr->consumer + 1);
+ packet = (pr->data + readindex);
+ // advance the consumer pointer last
+ pr->consumer = readindex;
+ if (pr->mutex_enable) {
+ // Signal the traffic thread assigned to this ring
+ // when the ring goes from having something to empty
+ if (pr->producer == pr->consumer) {
+#ifdef HAVE_THREAD_DEBUG
+ // thread_debug( "Consumer signal packet ring %p empty per %p", (void *)pr, (void *)&pr->awake_producer);
+#endif
+ assert(pr->awake_producer);
+ Condition_Signal(pr->awake_producer);
+ }
+ }
+ return packet;
+}
+
+inline void enqueue_ackring (struct PacketRing *pr, struct ReportStruct *metapacket) {
+ packetring_enqueue(pr, metapacket);
+ // Keep the latency low by signaling the consumer thread
+ // per each enqueue
+#ifdef HAVE_THREAD_DEBUG
+ // thread_debug( "Producer signal consumer ackring=%p per %p", (void *)pr, (void *)&pr->awake_consumer);
+#endif
+ Condition_Signal(pr->awake_consumer);
+}
+
+inline struct ReportStruct *dequeue_ackring (struct PacketRing *pr) {
+ struct ReportStruct *packet = NULL;
+ Condition_Lock((*(pr->awake_consumer)));
+ while ((packet = packetring_dequeue(pr)) == NULL) {
+ Condition_TimedWait(pr->awake_consumer, 1);
+ }
+ Condition_Unlock((*(pr->awake_consumer)));
+ if (packet) {
+ // Signal the producer thread for low latency
+ // indication of space available
+ Condition_Signal(pr->awake_producer);
+ }
+ return packet;
+}
+
+void packetring_free (struct PacketRing *pr) {
+ if (pr) {
+ if (pr->awaitcounter > 1000) fprintf(stderr, "WARN: Reporter thread may be too slow, await counter=%d, " \
+ "consider increasing NUM_REPORT_STRUCTS\n", pr->awaitcounter);
+ if (pr->data) {
+#ifdef HAVE_THREAD_DEBUG
+ Mutex_Lock(&packetringdebug_mutex);
+ totalpacketringcount--;
+ thread_debug("Free packet ring=%p producer=%p (consumer=%p) awaits = %d total rings = %d", \
+ (void *)pr, (void *) pr->awake_producer, (void *) pr->awake_consumer, pr->awaitcounter, totalpacketringcount);
+ Mutex_Unlock(&packetringdebug_mutex);
+#endif
+ free(pr->data);
+ }
+ free(pr);
+ }
+}
+
+void free_ackring(struct PacketRing *pr) {
+ packetring_free(pr);
+ Condition_Destroy(pr->awake_consumer);
+}
+
+/*
+ * This is an estimate and can be incorrect as these counters
+ * done like this is not thread safe. Use with care as there
+ * is no guarantee the return value is accurate
+ */
+#ifdef HAVE_THREAD_DEBUG
+inline int packetring_getcount (struct PacketRing *pr) {
+ int depth = 0;
+ if (pr->producer != pr->consumer) {
+ depth = (pr->producer > pr->consumer) ? \
+ (pr->producer - pr->consumer) : \
+ ((pr->maxcount - pr->consumer) + pr->producer);
+ // printf("DEBUG: Depth=%d for packet ring %p\n", depth, (void *)pr);
+ }
+ return depth;
+}
+#endif
diff --git a/src/pdfs.c b/src/pdfs.c
new file mode 100644
index 0000000..a1e0382
--- /dev/null
+++ b/src/pdfs.c
@@ -0,0 +1,100 @@
+/*---------------------------------------------------------------
+ * Copyright (c) 2017
+ * Broadcom Corporation
+ * All Rights Reserved.
+ *---------------------------------------------------------------
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software 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 name of Broadcom Coporation,
+ * 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.
+ * ________________________________________________________________
+ *
+ * pdfs.c
+ * Produce probability distribution functions, expected to be used
+ * for iperf client traffic emulations
+ *
+ * Implements the Polar form of the Box-Muller Transformation
+ *
+ * by Robert J. McMahon (rjmcmahon@rjmcmahon.com, bob.mcmahon@broadcom.com)
+ * & Tim Auckland
+ * -------------------------------------------------------------------
+ */
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <math.h>
+#include "headers.h"
+#include "pdfs.h"
+
+#define FALSE 0
+#define TRUE 1
+float box_muller(void) {
+ float x1, x2, w, y1;
+ static float y2;
+ static int generate = FALSE;
+ /* Each iteration produces two values, if one exists use the value from previous call */
+ generate = !generate;
+ if (!generate) {
+ y1 = y2;
+ } else {
+ int loopcontrol=100;
+ do {
+ x1 = 2.0 * (float)rand()/(float)(RAND_MAX) - 1.0;
+ x2 = 2.0 * (float)rand()/(float)(RAND_MAX) - 1.0;
+ w = x1 * x1 + x2 * x2;
+ } while ( w >= 1.0 && --loopcontrol > 0);
+ if (w >= 1.0) {
+ fprintf(stderr, "pdf box_muller() rand() error\n");
+ return 0;
+ } else {
+ w = sqrt( (-2.0 * logf( w ) ) / w );
+ y1 = x1 * w;
+ y2 = x2 * w;
+ }
+ }
+ return(y1);
+}
+
+float normal(float mean, float variance) {
+ return (box_muller() * variance + mean);
+}
+
+float lognormal(float mu, float sigma) {
+ float phi = sqrtf((mu * mu) + (sigma * sigma));
+ float mu_prime = logf(((mu * mu)/phi));
+ float sigma_prime = sqrtf(logf((phi * phi)/(mu * mu)));
+ return (expf(normal(mu_prime,sigma_prime)));
+}
diff --git a/src/socket_io.c b/src/socket_io.c
new file mode 100644
index 0000000..0475310
--- /dev/null
+++ b/src/socket_io.c
@@ -0,0 +1,239 @@
+/*---------------------------------------------------------------
+ * Copyright (c) 1999,2000,2001,2002,2003
+ * The Board of Trustees of the University of Illinois
+ * All Rights Reserved.
+ *---------------------------------------------------------------
+ * 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.
+ * ________________________________________________________________
+ * National Laboratory for Applied Network Research
+ * National Center for Supercomputing Applications
+ * University of Illinois at Urbana-Champaign
+ * http://www.ncsa.uiuc.edu
+ * ________________________________________________________________
+ *
+ * socket.c
+ * by Mark Gates <mgates@nlanr.net>
+ * -------------------------------------------------------------------
+ * set/getsockopt and read/write wrappers
+ * ------------------------------------------------------------------- */
+
+#include "headers.h"
+#include "util.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* -------------------------------------------------------------------
+ * Attempts to reads n bytes from a socket.
+ * Returns number actually read, or -1 on error.
+ * If number read < inLen then we reached EOF.
+ *
+ * from Stevens, 1998, section 3.9
+ * ------------------------------------------------------------------- */
+ssize_t readn (int inSock, void *outBuf, size_t inLen) {
+ size_t nleft;
+ ssize_t nread;
+ char *ptr;
+
+ assert(inSock >= 0);
+ assert(outBuf != NULL);
+ assert(inLen > 0);
+
+ ptr = (char*) outBuf;
+ nleft = inLen;
+
+ while (nleft > 0) {
+ nread = read(inSock, ptr, nleft);
+ if (nread < 0) {
+ if (errno == EINTR)
+ nread = 0; /* interupted, call read again */
+ else
+ return -1; /* error */
+ } else if (nread == 0)
+ break; /* EOF */
+
+ nleft -= nread;
+ ptr += nread;
+ }
+
+ return(inLen - nleft);
+} /* end readn */
+
+/* -------------------------------------------------------------------
+ * Similar to read but supports recv flags
+ * Returns number actually read, or -1 on error.
+ * If number read < inLen then we reached EOF.
+ * from Stevens, 1998, section 3.9
+ * ------------------------------------------------------------------- */
+int recvn (int inSock, char *outBuf, int inLen, int flags) {
+ int nleft;
+ int nread = 0;
+ char *ptr;
+
+ assert(inSock >= 0);
+ assert(outBuf != NULL);
+ assert(inLen > 0);
+
+ ptr = outBuf;
+ nleft = inLen;
+#if (HAVE_DECL_MSG_PEEK)
+ if (flags & MSG_PEEK) {
+ while ((nleft != nread) && !sInterupted) {
+ nread = recv(inSock, ptr, nleft, flags);
+ switch (nread) {
+ case SOCKET_ERROR :
+ // Note: use TCP fatal error codes even for UDP
+ if (FATALTCPREADERR(errno)) {
+ WARN_errno(1, "recvn peek");
+ nread = -1;
+ sInterupted = 1;
+ goto DONE;
+ }
+#ifdef HAVE_THREAD_DEBUG
+ WARN_errno(1, "recvn peek non-fatal");
+#endif
+ break;
+ case 0:
+#ifdef HAVE_THREAD_DEBUG
+ WARN(1, "recvn peek peer close");
+#endif
+ goto DONE;
+ break;
+ default :
+ break;
+ }
+ }
+ } else
+#endif
+ {
+ while ((nleft > 0) && !sInterupted) {
+#if (HAVE_DECL_MSG_WAITALL)
+ nread = recv(inSock, ptr, nleft, MSG_WAITALL);
+#else
+ nread = recv(inSock, ptr, nleft, 0);
+#endif
+ switch (nread) {
+ case SOCKET_ERROR :
+ // Note: use TCP fatal error codes even for UDP
+ if (FATALTCPREADERR(errno)) {
+ WARN_errno(1, "recvn");
+ nread = -1;
+ sInterupted = 1;
+ goto DONE;
+ } else {
+ nread = -2;
+ goto DONE;
+ }
+#ifdef HAVE_THREAD_DEBUG
+ WARN_errno(1, "recvn non-fatal");
+#endif
+ break;
+ case 0:
+#ifdef HAVE_THREAD_DEBUG
+ WARN(1, "recvn peer close");
+#endif
+ nread = inLen - nleft;
+ goto DONE;
+ break;
+ default :
+ nleft -= nread;
+ ptr += nread;
+ break;
+ }
+ nread = inLen - nleft;
+ }
+ }
+ DONE:
+ return(nread);
+} /* end readn */
+
+/* -------------------------------------------------------------------
+ * Attempts to write n bytes to a socket.
+ * returns number actually written, or -1 on error.
+ * number written is always inLen if there is not an error.
+ *
+ * from Stevens, 1998, section 3.9
+ * ------------------------------------------------------------------- */
+
+int writen (int inSock, const void *inBuf, int inLen, int *count) {
+ int nleft;
+ int nwritten;
+ const char *ptr;
+
+ assert(inSock >= 0);
+ assert(inBuf != NULL);
+ assert(inLen > 0);
+ assert(count != NULL);
+
+ ptr = (char*) inBuf;
+ nleft = inLen;
+ nwritten = 0;
+ *count = 0;
+
+ while ((nleft > 0) && !sInterupted) {
+ nwritten = write(inSock, ptr, nleft);
+ (*count)++;
+ switch (nwritten) {
+ case SOCKET_ERROR :
+ // check for a fatal error vs an error that should retry
+ if ((errno != EINTR) && (errno != EAGAIN) && (errno != EWOULDBLOCK)) {
+ nwritten = inLen - nleft;
+ fprintf(stdout, "FAIL: writen errno = %d\n", errno);
+ WARN_errno(1, "writen fatal");
+ sInterupted = 1;
+ goto DONE;
+ }
+ break;
+ case 0:
+ // write timeout - retry
+ break;
+ default :
+ nleft -= nwritten;
+ ptr += nwritten;
+ break;
+ }
+ nwritten = inLen - nleft;
+ }
+ DONE:
+ return (nwritten);
+} /* end writen */
+
+
+
+#ifdef __cplusplus
+} /* end extern "C" */
+#endif
diff --git a/src/stdio.c b/src/stdio.c
new file mode 100644
index 0000000..3bdf668
--- /dev/null
+++ b/src/stdio.c
@@ -0,0 +1,355 @@
+/*---------------------------------------------------------------
+ * Copyright (c) 1999,2000,2001,2002,2003
+ * The Board of Trustees of the University of Illinois
+ * All Rights Reserved.
+ *---------------------------------------------------------------
+ * 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.
+ * ________________________________________________________________
+ * National Laboratory for Applied Network Research
+ * National Center for Supercomputing Applications
+ * University of Illinois at Urbana-Champaign
+ * http://www.ncsa.uiuc.edu
+ * ________________________________________________________________
+ *
+ * stdio.c
+ * by Mark Gates <mgates@nlanr.net>
+ * and Ajay Tirumalla <tirumala@ncsa.uiuc.edu>
+ * -------------------------------------------------------------------
+ * input and output numbers, converting with kilo, mega, giga
+ * ------------------------------------------------------------------- */
+
+#include "headers.h"
+#include "util.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+const long kKilo_to_Unit = 1024;
+const long kMega_to_Unit = 1024 * 1024;
+const long kGiga_to_Unit = 1024 * 1024 * 1024;
+
+const long kkilo_to_Unit = 1000;
+const long kmega_to_Unit = 1000 * 1000;
+const long kgiga_to_Unit = 1000 * 1000 * 1000;
+
+/* -------------------------------------------------------------------
+ * byte_atof
+ *
+ * Given a string of form #x where # is a number and x is a format
+ * character listed below, this returns the interpreted integer.
+ * Gg, Mm, Kk are giga, mega, kilo respectively
+ * ------------------------------------------------------------------- */
+
+double byte_atof(const char *inString) {
+ double theNum;
+ char suffix = '\0';
+
+ assert(inString != NULL);
+
+ /* scan the number and any suffices */
+ sscanf(inString, "%lf%c", &theNum, &suffix);
+
+ /* convert according to [Gg Mm Kk] */
+ switch (suffix) {
+ case 'G': theNum *= kGiga_to_Unit; break;
+ case 'M': theNum *= kMega_to_Unit; break;
+ case 'K': theNum *= kKilo_to_Unit; break;
+ case 'g': theNum *= kgiga_to_Unit; break;
+ case 'm': theNum *= kmega_to_Unit; break;
+ case 'k': theNum *= kkilo_to_Unit; break;
+ default: break;
+ }
+ return theNum;
+} /* end byte_atof */
+
+/* -------------------------------------------------------------------
+ * byte_atoi
+ *
+ * Given a string of form #x where # is a number and x is a format
+ * character listed below, this returns the interpreted integer.
+ * Gg, Mm, Kk are giga, mega, kilo respectively
+ * ------------------------------------------------------------------- */
+
+intmax_t byte_atoi(const char *inString) {
+ double theNum;
+ char suffix = '\0';
+
+ assert(inString != NULL);
+
+ /* scan the number and any suffices */
+ sscanf(inString, "%lf%c", &theNum, &suffix);
+
+ /* convert according to [Gg Mm Kk] */
+ switch (suffix) {
+ case 'G': theNum *= kGiga_to_Unit; break;
+ case 'M': theNum *= kMega_to_Unit; break;
+ case 'K': theNum *= kKilo_to_Unit; break;
+ case 'g': theNum *= kgiga_to_Unit; break;
+ case 'm': theNum *= kmega_to_Unit; break;
+ case 'k': theNum *= kkilo_to_Unit; break;
+ default: break;
+ }
+ return (intmax_t) theNum;
+} /* end byte_atof */
+
+/* -------------------------------------------------------------------
+ * bitorbyte_atoi
+ *
+ * Given a string of form #x where # is a number and x is a format
+ * character listed below, this returns the interpreted integer.
+ * Gg, Mm, Kk are giga, mega, kilo respectively
+ * ------------------------------------------------------------------- */
+uintmax_t bitorbyte_atoi(const char *inString) {
+ double theNum;
+ char suffix = '\0';
+
+ assert(inString != NULL);
+
+ /* scan the number and any suffices */
+ sscanf(inString, "%lf%c", &theNum, &suffix);
+
+ /* convert according to [Gg Mm Kk] */
+ switch (suffix) {
+ case 'G': theNum *= (kgiga_to_Unit * 8.0); break;
+ case 'M': theNum *= (kmega_to_Unit * 8.0); break;
+ case 'K': theNum *= (kkilo_to_Unit * 8.0); break;
+ case 'g': theNum *= kgiga_to_Unit; break;
+ case 'm': theNum *= kmega_to_Unit; break;
+ case 'k': theNum *= kkilo_to_Unit; break;
+ default: break;
+ }
+ return (uintmax_t) theNum;
+} /* end byte_atof */
+
+/* -------------------------------------------------------------------
+ * bitorbyte_atof
+ *
+ * Given a string of form #x where # is a number and x is a format
+ * character listed below, this returns the interpreted integer.
+ * Gg, Mm, Kk are giga, mega, kilo respectively
+ * ------------------------------------------------------------------- */
+double bitorbyte_atof(const char *inString) {
+ double theNum;
+ char suffix = '\0';
+
+ assert(inString != NULL);
+
+ /* scan the number and any suffices */
+ sscanf(inString, "%lf%c", &theNum, &suffix);
+
+ /* convert according to [Gg Mm Kk] */
+ switch (suffix) {
+ case 'G': theNum *= (kgiga_to_Unit * 8.0); break;
+ case 'M': theNum *= (kmega_to_Unit * 8.0); break;
+ case 'K': theNum *= (kkilo_to_Unit * 8.0); break;
+ case 'g': theNum *= kgiga_to_Unit; break;
+ case 'm': theNum *= kmega_to_Unit; break;
+ case 'k': theNum *= kkilo_to_Unit; break;
+ case 'p':
+ case 'P': theNum *= -1; break;
+ default: break;
+ }
+ return theNum;
+} /* end byte_atof */
+
+/* -------------------------------------------------------------------
+ * constants for byte_printf
+ * ------------------------------------------------------------------- */
+
+/* used as indices into kConversion[], kLabel_Byte[], and kLabel_bit[] */
+enum {
+ kConv_Unit,
+ kConv_Kilo,
+ kConv_Mega,
+ kConv_Giga,
+ kConv_Tera,
+ kConv_Peta
+};
+
+/* factor to multiply the number by */
+const double kConversion[] =
+{
+ 1.0, /* unit */
+ 1.0 / 1024, /* kilo */
+ 1.0 / 1024 / 1024, /* mega */
+ 1.0 / 1024 / 1024 / 1024, /* giga */
+ 1.0 / 1024 / 1024 / 1024 / 1024, /* tera */
+ 1.0 / 1024 / 1024 / 1024 / 1024 / 1024 /* peta */
+};
+
+/* factor to multiply the number by for bits*/
+const double kConversionForBits[] =
+{
+ 1.0, /* unit */
+ 1.0 / 1000, /* kilo */
+ 1.0 / 1000 / 1000, /* mega */
+ 1.0 / 1000 / 1000 / 1000, /* giga */
+ 1.0 / 1000 / 1000 / 1000 / 1000, /* tera */
+ 1.0 / 1000 / 1000 / 1000 / 1000/ 1000 /* peta */
+};
+
+
+/* labels for Byte formats [KMG] */
+const char* kLabel_Byte[] =
+{
+ "Byte",
+ "KByte",
+ "MByte",
+ "GByte",
+ "TByte",
+ "PByte"
+};
+
+/* labels for bit formats [kmg] */
+const char* kLabel_bit[] =
+{
+ "bit",
+ "Kbit",
+ "Mbit",
+ "Gbit",
+ "Tbit",
+ "Pbit"
+};
+
+/* -------------------------------------------------------------------
+ * byte_snprintf
+ *
+ * Given a number in bytes and a format, converts the number and
+ * prints it out with a bits or bytes label.
+ * B, K, M, G, A, P, T for Byte, Kbyte, Mbyte, Gbyte, Tbyte, Pbyte adaptive byte
+ * b, k, m, g, a, p, t for bit, Kbit, Mbit, Gbit, Tbit, Pbit, adaptive bit
+ * adaptive picks the "best" one based on the number.
+ * outString should be at least 11 chars long
+ * (4 digits + space + 5 chars max + null)
+ * ------------------------------------------------------------------- */
+
+void byte_snprintf(char* outString, int inLen, double inNum, char inFormat) {
+ int conv = 0;
+ const char* suffix;
+ const char* format;
+ double tmpNum;
+
+ /* convert to bits for [bkmga] */
+ if (!isupper((int)inFormat)) {
+ inNum *= 8;
+ }
+
+ switch (toupper((int)inFormat)) {
+ case 'B': conv = kConv_Unit; break;
+ case 'K': conv = kConv_Kilo; break;
+ case 'M': conv = kConv_Mega; break;
+ case 'G': conv = kConv_Giga; break;
+ case 'T': conv = kConv_Tera; break;
+ case 'P': conv = kConv_Peta; break;
+
+ default:
+ case 'A': {
+ tmpNum = (inNum < 0.0 ? (-1 * inNum) : inNum);
+ conv = kConv_Unit;
+
+ if (isupper((int)inFormat)) {
+ while (tmpNum >= 1024.0 && conv < kConv_Peta) {
+ tmpNum /= 1024.0;
+ conv++;
+ }
+ } else {
+ while (tmpNum >= 1000.0 && conv < kConv_Peta) {
+ tmpNum /= 1000.0;
+ conv++;
+ }
+ }
+ break;
+ }
+ }
+
+ if (!isupper((int)inFormat)) {
+ inNum *= kConversionForBits[conv];
+ suffix = kLabel_bit[conv];
+ } else {
+ inNum *= kConversion[conv];
+ suffix = kLabel_Byte[conv];
+ }
+
+ /* print such that we always fit in 4 places */
+ tmpNum = (inNum < 0.0 ? (-1 * inNum) : inNum);
+ if (tmpNum < 0.9995) { /* 0.995 would be rounded to 1.000 */
+ format = "%4.3f %s"; /* #.## */
+ } else if (tmpNum < 9.995) { /* 9.995 would be rounded to 10.00 */
+ format = "%4.2f %s"; /* #.## */
+ } else if (tmpNum < 99.95) { /* 99.95 would be rounded to 100.0 */
+ format = "%4.1f %s"; /* ##.# */
+ } else if (tmpNum < 999.5) { /* 999.5 would be rounded to 1000 */
+ format = "%4.0f %s"; /* ### */
+ } else { /* 1000-1024 fits in 4 places
+ * If not using Adaptive sizes then
+ * this code will not control spaces*/
+ format = "%4.0f %s"; /* #### */
+ }
+ snprintf(outString, inLen, format, inNum, suffix);
+} /* end byte_snprintf */
+
+/* -------------------------------------------------------------------
+ * redirect
+ *
+ * redirect the stdout into a specified file
+ * return: none
+ * ------------------------------------------------------------------- */
+
+void redirect(const char *inOutputFileName) {
+#ifdef WIN32
+
+ FILE *fp;
+
+ if (inOutputFileName == NULL) {
+ fprintf(stderr, "should specify the output file name.\n");
+ return;
+ }
+
+ fp = freopen(inOutputFileName, "a+", stdout);
+ if (fp == NULL) {
+ fprintf(stderr, "redirect stdout failed!\n");
+ return;
+ }
+
+#endif
+
+}
+
+
+#ifdef __cplusplus
+} /* end extern "C" */
+#endif
diff --git a/src/tcp_window_size.c b/src/tcp_window_size.c
new file mode 100644
index 0000000..4b5d79b
--- /dev/null
+++ b/src/tcp_window_size.c
@@ -0,0 +1,227 @@
+/*---------------------------------------------------------------
+ * Copyright (c) 1999,2000,2001,2002,2003
+ * The Board of Trustees of the University of Illinois
+ * All Rights Reserved.
+ *---------------------------------------------------------------
+ * 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.
+ * ________________________________________________________________
+ * National Laboratory for Applied Network Research
+ * National Center for Supercomputing Applications
+ * University of Illinois at Urbana-Champaign
+ * http://www.ncsa.uiuc.edu
+ * ________________________________________________________________
+ *
+ * tcp_window_size.c
+ * by Mark Gates <mgates@nlanr.net>
+ * -------------------------------------------------------------------
+ * set/getsockopt
+ * ------------------------------------------------------------------- */
+
+#include "headers.h"
+
+#include "util.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* -------------------------------------------------------------------
+ * If inTCPWin > 0, set the TCP window size (via the socket buffer
+ * sizes) for inSock. Otherwise leave it as the system default.
+ *
+ * This must be called prior to calling listen() or connect() on
+ * the socket, for TCP window sizes > 64 KB to be effective.
+ *
+ * This now works on UNICOS also, by setting TCP_WINSHIFT.
+ * This now works on AIX, by enabling RFC1323.
+ * returns -1 on error, 0 on no error.
+ * ------------------------------------------------------------------- */
+
+int setsock_tcp_windowsize (int inSock, int inTCPWin, int inSend) {
+#ifdef SO_SNDBUF
+ int rc;
+ int newTCPWin;
+
+ assert(inSock >= 0);
+ if (inTCPWin > 0) {
+#ifdef TCP_WINSHIFT
+ /* UNICOS requires setting the winshift explicitly */
+ if (inTCPWin > 65535) {
+ int winShift = 0;
+ int scaledWin = inTCPWin >> 16;
+ while (scaledWin > 0) {
+ scaledWin >>= 1;
+ winShift++;
+ }
+
+ /* set TCP window shift */
+ rc = setsockopt(inSock, IPPROTO_TCP, TCP_WINSHIFT,
+ (char*) &winShift, sizeof(winShift));
+ if (rc < 0) {
+ return rc;
+ }
+
+ /* Note: you cannot verify TCP window shift, since it returns
+ * a structure and not the same integer we use to set it. (ugh) */
+ }
+#endif /* TCP_WINSHIFT */
+
+#ifdef TCP_RFC1323
+ /* On AIX, RFC 1323 extensions can be set system-wide,
+ * using the 'no' network options command. But we can also set them
+ * per-socket, so let's try just in case. */
+ if (inTCPWin > 65535) {
+ /* enable RFC 1323 */
+ int on = 1;
+ rc = setsockopt(inSock, IPPROTO_TCP, TCP_RFC1323,
+ (char*) &on, sizeof(on));
+ if (rc < 0) {
+ return rc;
+ }
+ }
+#endif /* TCP_RFC1323 */
+
+ if (!inSend) {
+ /* receive buffer -- set
+ * note: results are verified after connect() or listen(),
+ * since some OS's don't show the corrected value until then. */
+ newTCPWin = inTCPWin;
+ rc = setsockopt(inSock, SOL_SOCKET, SO_RCVBUF,
+ (char*) &newTCPWin, sizeof(newTCPWin));
+ } else {
+ /* send buffer -- set
+ * note: results are verified after connect() or listen(),
+ * since some OS's don't show the corrected value until then. */
+ newTCPWin = inTCPWin;
+ rc = setsockopt(inSock, SOL_SOCKET, SO_SNDBUF,
+ (char*) &newTCPWin, sizeof(newTCPWin));
+ }
+ if (rc < 0) {
+ return rc;
+ }
+ }
+#endif /* SO_SNDBUF */
+ return 0;
+} /* end setsock_tcp_windowsize */
+
+/* -------------------------------------------------------------------
+ * returns the TCP window size (on the sending buffer, SO_SNDBUF),
+ * or -1 on error.
+ * ------------------------------------------------------------------- */
+
+int getsock_tcp_windowsize (int inSock, int inSend) {
+ int rc = -1;
+#ifdef SO_SNDBUF
+ Socklen_t len;
+ int theTCPWin = 0;
+ /* send buffer -- query for buffer size */
+ len = sizeof(theTCPWin);
+ if (inSend) {
+ rc = getsockopt(inSock, SOL_SOCKET, SO_SNDBUF,
+ (char*) &theTCPWin, &len);
+ } else {
+ rc = getsockopt(inSock, SOL_SOCKET, SO_RCVBUF,
+ (char*) &theTCPWin, &len);
+ }
+ if (rc == 0) {
+ rc = theTCPWin;
+ }
+#endif
+ return rc;
+} /* end getsock_tcp_windowsize */
+
+#if HAVE_DECL_TCP_WINDOW_CLAMP
+int setsock_tcp_windowclamp (int inSock, int clampsize) {
+ assert(inSock >= 0);
+ assert(clampsize > 0);
+ int rc;
+ Socklen_t len;
+
+ len = sizeof(clampsize);
+ rc = setsockopt(inSock, IPPROTO_TCP, TCP_WINDOW_CLAMP, (char*)(&clampsize), len);
+ WARN_errno(rc == SOCKET_ERROR, "setsockopt TCP_WINDOW_CLAMP");
+ return rc;
+}
+
+int getsock_tcp_windowclamp (int inSock) {
+ assert(inSock >= 0);
+ int clamp = 0;
+ int rc;
+ Socklen_t len;
+
+ /* send buffer -- query for buffer size */
+ len = sizeof(clamp);
+ rc = getsockopt(inSock, IPPROTO_TCP, TCP_WINDOW_CLAMP, (char*)(&clamp), &len);
+ WARN_errno(rc == SOCKET_ERROR, "getsockopt TCP_WINDOW_CLAMP");
+ if (rc < 0) {
+ return rc;
+ }
+ return clamp;
+}
+#endif
+
+#if HAVE_DECL_TCP_NOTSENT_LOWAT
+int setsock_tcp_notsent_low_watermark (int inSock, int watermark) {
+ assert(inSock >= 0);
+ assert(watermark > 0);
+ int rc;
+ Socklen_t len;
+
+ len = sizeof(watermark);
+ rc = setsockopt(inSock, IPPROTO_TCP, TCP_NOTSENT_LOWAT, (char*)(&watermark), len);
+ WARN_errno(rc == SOCKET_ERROR, "setsockopt TCP_NOTSENT_LOWAT");
+ return rc;
+}
+
+int getsock_tcp_notsent_low_watermark (int inSock) {
+ assert(inSock >= 0);
+ int watermark = 0;
+ int rc;
+ Socklen_t len;
+
+ /* send buffer -- query for buffer size */
+ len = sizeof(watermark);
+ rc = getsockopt(inSock, IPPROTO_TCP, TCP_NOTSENT_LOWAT, (char*)(&watermark), &len);
+ WARN_errno(rc == SOCKET_ERROR, "getsockopt TCP_NOTSENT_LOWAT");
+ if (rc < 0) {
+ return rc;
+ }
+ return watermark;
+}
+#endif
+#ifdef __cplusplus
+} /* end extern "C" */
+#endif