From 7c720bec5600a9e607c875c670ca30ed351fa4ba Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Mon, 15 Apr 2024 20:20:54 +0200 Subject: Adding upstream version 2.1.9+dfsg. Signed-off-by: Daniel Baumann --- src/Client.cpp | 1871 +++++++++++++++++++++++++++++++++++ src/Extractor.c | 188 ++++ src/Launch.cpp | 478 +++++++++ src/Listener.cpp | 1447 +++++++++++++++++++++++++++ src/Locale.c | 757 ++++++++++++++ src/Makefile.am | 71 ++ src/Makefile.in | 829 ++++++++++++++++ src/PerfSocket.cpp | 445 +++++++++ src/ReportOutputs.c | 2019 +++++++++++++++++++++++++++++++++++++ src/Reporter.c | 1937 ++++++++++++++++++++++++++++++++++++ src/Reports.c | 1091 ++++++++++++++++++++ src/Server.cpp | 946 ++++++++++++++++++ src/Settings.cpp | 2577 ++++++++++++++++++++++++++++++++++++++++++++++++ src/SocketAddr.c | 941 ++++++++++++++++++ src/active_hosts.cpp | 261 +++++ src/checkdelay.c | 203 ++++ src/checkisoch.cpp | 129 +++ src/checkpdfs.c | 217 ++++ src/checksums.c | 229 +++++ src/dscp.c | 180 ++++ src/gnu_getopt.c | 818 +++++++++++++++ src/gnu_getopt_long.c | 162 +++ src/histogram.c | 269 +++++ src/igmp_querier.c | 213 ++++ src/iperf_formattime.c | 100 ++ src/isochronous.cpp | 275 ++++++ src/main.cpp | 479 +++++++++ src/packet_ring.c | 219 ++++ src/pdfs.c | 100 ++ src/socket_io.c | 239 +++++ src/stdio.c | 355 +++++++ src/tcp_window_size.c | 227 +++++ 32 files changed, 20272 insertions(+) create mode 100644 src/Client.cpp create mode 100644 src/Extractor.c create mode 100644 src/Launch.cpp create mode 100644 src/Listener.cpp create mode 100644 src/Locale.c create mode 100644 src/Makefile.am create mode 100644 src/Makefile.in create mode 100644 src/PerfSocket.cpp create mode 100644 src/ReportOutputs.c create mode 100644 src/Reporter.c create mode 100644 src/Reports.c create mode 100644 src/Server.cpp create mode 100644 src/Settings.cpp create mode 100644 src/SocketAddr.c create mode 100644 src/active_hosts.cpp create mode 100644 src/checkdelay.c create mode 100644 src/checkisoch.cpp create mode 100644 src/checkpdfs.c create mode 100644 src/checksums.c create mode 100644 src/dscp.c create mode 100644 src/gnu_getopt.c create mode 100644 src/gnu_getopt_long.c create mode 100644 src/histogram.c create mode 100644 src/igmp_querier.c create mode 100644 src/iperf_formattime.c create mode 100644 src/isochronous.cpp create mode 100644 src/main.cpp create mode 100644 src/packet_ring.c create mode 100644 src/pdfs.c create mode 100644 src/socket_io.c create mode 100644 src/stdio.c create mode 100644 src/tcp_window_size.c (limited to 'src') 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 + * ------------------------------------------------------------------- + * A client thread initiates a connect to the server and handles + * sending and receiving data, then closes the socket. + * ------------------------------------------------------------------- */ +#include +#include +#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(&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(&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(&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(&mSettings->local), &mSettings->size_local); + getpeername(mySocket, reinterpret_cast(&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(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(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(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(round(250000.0 / mSettings->mFPS)); + } else if (mSettings->mInterval > 0) { + sosndtimer = static_cast(round(0.5 * mSettings->mInterval)); + } else { + sosndtimer = static_cast(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(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(mSettings->mBufLen)) ? mSettings->mAmount : mSettings->mBufLen); + } + if (isburst && !(burst_remaining > 0)) { + if (isIsochronous(mSettings)) { + assert(mSettings->mMean); + burst_remaining = static_cast(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(sizeof(struct TCP_burst_payload))) + burst_remaining = static_cast(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(reportstruct->packetLen)) { + mSettings->mAmount -= static_cast(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(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(reportstruct->packetLen)) { + mSettings->mAmount -= static_cast(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(std::ceil(static_cast(reportstruct->tcpstats.rtt) * mSettings->rtt_nearcongest_weight_factor)); +#else + pacing_timer = static_cast(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(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(reportstruct->packetLen)) { + mSettings->mAmount -= static_cast(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(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(reportstruct->packetLen)) { + mSettings->mAmount -= static_cast(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(round(mSettings->mInterval / 2.0)); + SetSocketOptionsReceiveTimeout(mSettings, sotimer); + SetSocketOptionsSendTimeout(mSettings, sotimer); + } else if (isModeTime(mSettings)) { + int sotimer = static_cast(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(&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(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(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(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(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(currLen)) { + mSettings->mAmount -= static_cast(currLen); + } else { + mSettings->mAmount = 0; + } + } + + // report packets + reportstruct->packetLen = static_cast(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(delay / 1000)); + } + } + FinishTrafficActions(); +} + +/* + * UDP isochronous send loop + */ +void Client::RunUDPIsochronous () { + struct UDP_datagram* mBuf_UDP = reinterpret_cast(mSettings->mBuf); + // skip over the UDP datagram (seq no, timestamp) to reach the isoch fields + struct client_udp_testhdr *udp_payload = reinterpret_cast(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(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(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(currLen)) { + mSettings->mAmount -= static_cast(currLen); + } else { + mSettings->mAmount = 0; + } + } + // report packets + + reportstruct->frameID=frameid; + reportstruct->packetLen = static_cast(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(delay / 1000)); + } + } + } + FinishTrafficActions(); +} +// end RunUDPIsoch + +inline void Client::WritePacketID (intmax_t packetID) { + struct UDP_datagram * mBuf_UDP = reinterpret_cast(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(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(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(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(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(reinterpret_cast(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(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(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(&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(&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(&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 + * ------------------------------------------------------------------- + * 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(&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 + * & Ajay Tirumala + * 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 + * + * + * + * + * + * + * + * + * + * + * + * ------------------------------------------------------------------- */ +#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(mSettings->mListenerTimeout); + timeout.tv_usec = (static_cast(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(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(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(&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(&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(&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(&mreq), sizeof(mreq)); +#else + int rc = setsockopt(ListenSocket, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, \ + reinterpret_cast(&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(&group_source_req.gsr_group); + source=reinterpret_cast(&group_source_req.gsr_source); + source->sin6_family = AF_INET6; + group->sin6_family = AF_INET6; + /* Set the group */ + rc=getsockname(ListenSocket,reinterpret_cast(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(&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(&group_req.gr_group); + group->sin6_family = AF_INET6; + /* Set the group */ + rc=getsockname(ListenSocket,reinterpret_cast(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(&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(&group_source_req.gsr_group); + source=reinterpret_cast(&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(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(&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(&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(&group_req.gr_group); + group->sin_family = AF_INET; + /* Set the group */ + rc=getsockname(ListenSocket,reinterpret_cast(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(&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(&server->peer); + struct sockaddr *l = reinterpret_cast(&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(l))->sin6_port, (reinterpret_cast(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(l))->sin6_port, (reinterpret_cast(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(l))->sin_addr.s_addr, (reinterpret_cast(p))->sin_addr.s_addr, (reinterpret_cast(l))->sin_port, (reinterpret_cast(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(&server->peer); + struct sockaddr *l = reinterpret_cast(&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(l))->sin6_port, (reinterpret_cast(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(l))->sin6_port, (reinterpret_cast(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(l))->sin_addr.s_addr, (reinterpret_cast(p))->sin_addr.s_addr, (reinterpret_cast(l))->sin_port, (reinterpret_cast(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(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(&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(ntohl(mBuf_UDP->id))) | (static_cast(ntohl(mBuf_UDP->id2)) << 32); + } else { + // Old client - Signed PacketID in Signed id + packetID = static_cast(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(&server->peer), server->size_peer); + FAIL_errno(rc == SOCKET_ERROR, "connect UDP", mSettings); + server->size_local = sizeof(iperf_sockaddr); + getsockname(server->mSock, reinterpret_cast(&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(&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(&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(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(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(&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(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(server->mBuf); + uint32_t flags = ntohl(hdr->base.flags); + if (flags & HEADER_BOUNCEBACK) { + if (!isServerModeTime(server)) { + unsetModeTime(server); + } + struct bounceback_hdr *bbhdr = reinterpret_cast(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(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(server->mBuf); + server->mFPS = ntohl(hdr->isoch_settings.FPSl); + server->mFPS += ntohl(hdr->isoch_settings.FPSu) / static_cast(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((server->mInterval) / 4); + } else if (isModeTime(server)) { + sotimer = static_cast((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(&optflag), sizeof(int))) < 0) { + WARN_errno(rc < 0, "tcpnodelay"); + } +#endif + if ((rc = send(server->mSock, reinterpret_cast(&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(&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 + * & Mark Gates + * ------------------------------------------------------------------- + * 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 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 [:][%] bind to , 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 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 #[] 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 [%] 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 run in client mode, connecting to \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 :, 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 [ | ] bind ip (and optional port) from which to source traffic\n\ + -F, --fileinput input the data to be transmitted from a file\n\ + -H, --ssm-host 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_, such as IPERF_BANDWIDTH.\n\ +\n\ +Source at \n\ +Report bugs to \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= 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 + * Ajay Tirumala + * ------------------------------------------------------------------- + * 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 + * + * + * + * + * + * + * + * + * + * + * + * + * ------------------------------------------------------------------- */ +#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(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(&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(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(&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(&val), static_cast(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(&val), static_cast(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(&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(&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(&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(&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(&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(&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(&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(&reqtos), len); + WARN_errno(rc == SOCKET_ERROR, "setsockopt IP_TOS"); + rc = getsockopt(mSettings->mSock, IPPROTO_IP, IP_TOS, + reinterpret_cast(&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 +#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= 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 + * + * Major rewrite by Robert McMahon (Sept 2020, ver 2.0.14) + * ________________________________________________________________ */ + +#include +#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 +#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 + * 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 +#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(round(mSettings->mInterval / 2.0)); + } else if (isServerModeTime(mSettings)) { + sorcvtimer = static_cast(round(mSettings->mAmount * 10000) / 2); + } + isburst = (isIsochronous(mSettings) || isPeriodicBurst(mSettings) || (isTripTime(mSettings) && !isUDP(mSettings))); + if (isburst && (mSettings->mFPS > 0.0)) { + sorcvtimer = static_cast(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(&opt), len); + WARN_errno(rc == SOCKET_ERROR, "setsockopt TCP_QUICKACK"); + } +#endif + if (isburst && (burst_nleft == 0)) { + if ((n = recvn(mSettings->mSock, reinterpret_cast(&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(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(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(&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(round(mSettings->mInterval / 2.0)); + SetSocketOptionsSendTimeout(mSettings, sotimer); + SetSocketOptionsReceiveTimeout(mSettings, sotimer); + } else if (isModeTime(mSettings)) { + int sotimer = static_cast(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(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(&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, ×tampOn, 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(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(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(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(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(&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(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(ntohl(mBuf_UDP->id))) | (static_cast(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(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(mSettings->mBuf); + ip_hdr = reinterpret_cast(mSettings->mBuf + sizeof(struct ether_header)); + // L4 offest is set by the listener and depends upon IPv4 or IPv6 + udp_hdr = reinterpret_cast(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(&mSettings->peer); + struct sockaddr *l = reinterpret_cast(&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(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(mSettings->mBuf + mSettings->l4offset); + + // Check plain old v4 using v4 addr structs + if (l->sa_family == AF_INET) { + data = reinterpret_cast(mSettings->mBuf + sizeof(struct ether_header) + IPV4SRCOFFSET); + if ((reinterpret_cast(p))->sin_addr.s_addr != *data++) + return -1; + if ((reinterpret_cast(l))->sin_addr.s_addr != *data) + return -1; + if (udp_hdr->source != (reinterpret_cast(p))->sin_port) + return -1; + if (udp_hdr->dest != (reinterpret_cast(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(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(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(p))->sin6_port) + return -1; + if (udp_hdr->dest != (reinterpret_cast(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(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(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 + * & Ajay Tirumala + * ------------------------------------------------------------------- + * Stores and parses the initial values for all the global variables. + * ------------------------------------------------------------------- + * headers + * uses + * + * + * + * + * + * ------------------------------------------------------------------- */ + +#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 + +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, ¬cpbbquickack, 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(calloc(strlen(from->mIfrname) + 1, sizeof(char))); + strcpy((*into)->mIfrname, from->mIfrname); + } + if (from->mIfrnametx != NULL) { + (*into)->mIfrnametx = static_cast(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(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(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("")+1]; + strcpy(mExtSettings->mFileName,""); + 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(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(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(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(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(mExtSettings->mPermitKeyTime.tv_sec), usecs); + snprintf(mExtSettings->mPermitKey, (timestrlength+1), "%ld.%06d-", static_cast(mExtSettings->mPermitKeyTime.tv_sec), usecs); + srand(static_cast(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(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 (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 (mExtSettings->mBurstSize) == 0) { + mExtSettings->mBurstSize = byte_atoi("1M"); //default to 1 Mbyte + } + if (static_cast (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 (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(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(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(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 :, 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 :, (e.g. 60:18M,1m)\n"); + } + } + } + + // Check for further mLocalhost (-B) and 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(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(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(rMillion); + (*client)->mMean = ntohl(hdr->Meanl); + (*client)->mMean += ntohl(hdr->Meanu) / static_cast(rMillion); + (*client)->mVariance = ntohl(hdr->Variancel); + (*client)->mVariance += ntohl(hdr->Varianceu) / static_cast(rMillion); + (*client)->mBurstIPG = ntohl(hdr->BurstIPGl); + (*client)->mBurstIPG += ntohl(hdr->BurstIPGu) / static_cast(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(rMillion); +} + +void Settings_ReadClientSettingsV1 (struct thread_Settings **client, struct client_hdr_v1 *hdr) { + (*client)->mTID = thread_zeroid(); + (*client)->mPort = static_cast(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(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(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(ntohl(hdr->start_fq.fqrateu)) << 32); +#endif + } + } + } else { //tcp first payload + struct client_tcp_testhdr *hdr = static_cast(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(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(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(&server->peer))->sa_family == AF_INET) { + inet_ntop(AF_INET, &(reinterpret_cast(&server->peer))->sin_addr, + reversed_thread->mHost, REPORT_ADDRLEN); + } +#if HAVE_IPV6 + else { + inet_ntop(AF_INET6, &(reinterpret_cast(&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(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(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(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(strnlen(client->mPermitKey, MAX_PERMITKEY_LEN)); + flags |= HEADER_KEYCHECK; + struct permitKey *thiskey = reinterpret_cast(static_cast(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((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 + * and Mark Gates + * ------------------------------------------------------------------- */ + +#define HEADERS() + +#include "headers.h" + +#include "SocketAddr.h" +#ifdef HAVE_IFADDRS_H +#include +#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: (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 % (server), -c % (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 +#include +#include +#include +#include +#include "headers.h" +#include "util.h" +#include "delay.h" +#if HAVE_SCHED_SETSCHEDULER +#include +#ifdef HAVE_MLOCKALL +#include +#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 +#include +#include +#include +#include +#include +#include +#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 -f -m -v \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 +#include +#include +#include +#include +#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 +#endif + +#include +#if HAVE_STRING_H +#include +#endif +#if HAVE_STDLIB_H +#include +#endif + +#if HAVE_INTTYPES_H +#include +#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 +#endif +#include +#endif +#if HAVE_NETINET_IP_H +#include +#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 + * 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 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 +#include +#ifndef _MSC_VER /* Visual C++ doesn't have unistd.h */ + #include +#endif +#include + +#ifndef _ +/* This is for other GNU distributions with internationalized messages. + When compiling libc, the _ macro is predefined. */ + #ifdef HAVE_LIBINTL_H + #include + #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 + * 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 +#include + +#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 + +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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 , -d daemon, -g , -t \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(×tamp.tv_sec) : *localtime(×tamp.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(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(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(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 + * & Ajay Tirumala + * ------------------------------------------------------------------- + * main does initialization and creates the various objects that will + * actually run the iperf program, then waits in the Joinall(). + * ------------------------------------------------------------------- + * headers + * uses + * + * + * + * + * ------------------------------------------------------------------- */ + +#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 +#include +#include +#include +#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 + * ------------------------------------------------------------------- + * 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 + * and Ajay Tirumalla + * ------------------------------------------------------------------- + * 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 + * ------------------------------------------------------------------- + * 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 -- cgit v1.2.3