summaryrefslogtreecommitdiffstats
path: root/src/Settings.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/Settings.cpp')
-rw-r--r--src/Settings.cpp2577
1 files changed, 2577 insertions, 0 deletions
diff --git a/src/Settings.cpp b/src/Settings.cpp
new file mode 100644
index 0000000..39229f5
--- /dev/null
+++ b/src/Settings.cpp
@@ -0,0 +1,2577 @@
+/*---------------------------------------------------------------
+ * Copyright (c) 1999,2000,2001,2002,2003
+ * The Board of Trustees of the University of Illinois
+ * All Rights Reserved.
+ *---------------------------------------------------------------
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software (Iperf) and associated
+ * documentation files (the "Software"), to deal in the Software
+ * without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute,
+ * sublicense, and/or sell copies of the Software, and to permit
+ * persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ *
+ * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and
+ * the following disclaimers.
+ *
+ *
+ * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimers in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ *
+ * Neither the names of the University of Illinois, NCSA,
+ * nor the names of its contributors may be used to endorse
+ * or promote products derived from this Software without
+ * specific prior written permission.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE CONTIBUTORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ * ________________________________________________________________
+ * National Laboratory for Applied Network Research
+ * National Center for Supercomputing Applications
+ * University of Illinois at Urbana-Champaign
+ * http://www.ncsa.uiuc.edu
+ * ________________________________________________________________
+ *
+ * Settings.cpp
+ * by Mark Gates <mgates@nlanr.net>
+ * & Ajay Tirumala <tirumala@ncsa.uiuc.edu>
+ * -------------------------------------------------------------------
+ * Stores and parses the initial values for all the global variables.
+ * -------------------------------------------------------------------
+ * headers
+ * uses
+ * <stdlib.h>
+ * <stdio.h>
+ * <string.h>
+ *
+ * <unistd.h>
+ * ------------------------------------------------------------------- */
+
+#define HEADERS()
+
+#include "headers.h"
+#include "Settings.hpp"
+#include "Locale.h"
+#include "SocketAddr.h"
+#include "util.h"
+#include "version.h"
+#include "gnu_getopt.h"
+#include "isochronous.hpp"
+#include "pdfs.h"
+#include "payloads.h"
+#include "PerfSocket.hpp"
+#include "dscp.h"
+#include "iperf_formattime.h"
+#include <math.h>
+
+static int reversetest = 0;
+static int fullduplextest = 0;
+static int histogram = 0;
+static int jitter_histogram = 0;
+static int l2checks = 0;
+static int incrdstip = 0;
+static int incrsrcip = 0;
+static int incrdstport = 0;
+static int incrsrcport = 0;
+static int sumdstip = 0;
+static int txstarttime = 0;
+static int noconnectsync = 0;
+static int txholdback = 0;
+static int fqrate = 0;
+static int triptime = 0;
+static int infinitetime = 0;
+static int connectonly = 0;
+static int connectretry = 0;
+static int burstipg = 0;
+static int burstsize = 0;
+static int burstperiodic = 0;
+static int isochronous = 0;
+static int noudpfin = 0;
+static int numreportstructs = 0;
+static int sumonly = 0;
+static int so_dontroute = 0;
+static int nearcongest = 0;
+static int permitkey = 0;
+static int permitkeytimeout = 0;
+static int rxwinclamp = 0;
+static int txnotsentlowwater = 0;
+static int tapif = 0;
+static int tunif = 0;
+static int hideips = 0;
+static int bounceback = 0;
+static int bouncebackhold = 0;
+static int bouncebackperiod = 0;
+static int overridetos = 0;
+static int notcpbbquickack = 0;
+static int tcpquickack = 0;
+static int notcpbbquickack_cliset = 0;
+static int workingload = 0;
+static int utctimes = 0;
+static int bouncebackdelaystart = 0;
+static int tcpwritetimes = 0;
+
+void Settings_Interpret(char option, const char *optarg, struct thread_Settings *mExtSettings);
+// apply compound settings after the command line has been fully parsed
+void Settings_ModalOptions(struct thread_Settings *mExtSettings);
+
+static void generate_permit_key(struct thread_Settings *mExtSettings);
+
+/* -------------------------------------------------------------------
+ * command line options
+ *
+ * The option struct essentially maps a long option name (--foobar)
+ * or environment variable ($FOOBAR) to its short option char (f).
+ * ------------------------------------------------------------------- */
+#define LONG_OPTIONS()
+
+const struct option long_options[] =
+{
+{"singleclient", no_argument, NULL, '1'},
+#if 0
+{"v4", no_argument, NULL, '4'},
+{"v6", no_argument, NULL, '6'},
+#endif
+{"bandwidth", required_argument, NULL, 'b'},
+{"client", required_argument, NULL, 'c'},
+{"dualtest", no_argument, NULL, 'd'},
+{"enhanced", no_argument, NULL, 'e'},
+{"format", required_argument, NULL, 'f'},
+{"help", no_argument, NULL, 'h'},
+{"interval", required_argument, NULL, 'i'},
+{"len", required_argument, NULL, 'l'},
+{"print_mss", no_argument, NULL, 'm'},
+{"num", required_argument, NULL, 'n'},
+{"output", required_argument, NULL, 'o'},
+{"port", required_argument, NULL, 'p'},
+{"tradeoff", no_argument, NULL, 'r'},
+{"server", no_argument, NULL, 's'},
+{"time", required_argument, NULL, 't'},
+{"udp", no_argument, NULL, 'u'},
+{"version", no_argument, NULL, 'v'},
+{"window", required_argument, NULL, 'w'},
+{"reportexclude", required_argument, NULL, 'x'},
+{"reportstyle",required_argument, NULL, 'y'},
+{"realtime", no_argument, NULL, 'z'},
+
+// more esoteric options
+{"awdl", no_argument, NULL, 'A'},
+{"bind", required_argument, NULL, 'B'},
+{"bounceback", optional_argument, &bounceback, 1},
+{"bounceback-txdelay", required_argument, &bouncebackdelaystart, 1},
+{"bounceback-hold", required_argument, &bouncebackhold, 1},
+{"bounceback-no-quickack", no_argument, &notcpbbquickack, 1},
+{"bounceback-period", required_argument, &bouncebackperiod, 1},
+{"compatibility", no_argument, NULL, 'C'},
+{"daemon", no_argument, NULL, 'D'},
+{"file_input", required_argument, NULL, 'F'},
+{"ssm-host", required_argument, NULL, 'H'},
+{"stdin_input", no_argument, NULL, 'I'},
+{"mss", required_argument, NULL, 'M'},
+{"nodelay", no_argument, NULL, 'N'},
+{"listenport", required_argument, NULL, 'L'},
+{"parallel", required_argument, NULL, 'P'},
+#ifdef WIN32
+{"remove", no_argument, NULL, 'R'},
+#else
+{"reverse", no_argument, NULL, 'R'},
+#endif
+{"tos", required_argument, NULL, 'S'},
+{"ttl", required_argument, NULL, 'T'},
+{"single_udp", no_argument, NULL, 'U'},
+{"ipv6_domain", no_argument, NULL, 'V'},
+{"suggest_win_size", no_argument, NULL, 'W'},
+{"peer-detect", no_argument, NULL, 'X'},
+{"tcp-congestion", required_argument, NULL, 'Z'},
+{"histograms", optional_argument, &histogram, 1},
+{"hide-ips", no_argument, &hideips, 1},
+{"jitter-histograms", optional_argument, &jitter_histogram, 1},
+{"udp-histograms", optional_argument, &histogram, 1}, // keep support per 2.0.13 usage
+{"l2checks", no_argument, &l2checks, 1},
+{"incr-dstip", no_argument, &incrdstip, 1},
+{"incr-srcip", no_argument, &incrsrcip, 1},
+{"incr-dstport", no_argument, &incrdstport, 1},
+{"incr-srcport", no_argument, &incrsrcport, 1},
+{"sum-dstip", no_argument, &sumdstip, 1},
+{"txstart-time", required_argument, &txstarttime, 1},
+{"txdelay-time", required_argument, &txholdback, 1},
+{"fq-rate", required_argument, &fqrate, 1},
+{"trip-times", no_argument, &triptime, 1},
+{"no-udp-fin", no_argument, &noudpfin, 1},
+{"connect-only", optional_argument, &connectonly, 1},
+{"connect-retries", required_argument, &connectretry, 1},
+{"no-connect-sync", no_argument, &noconnectsync, 1},
+{"full-duplex", no_argument, &fullduplextest, 1},
+{"ipg", required_argument, &burstipg, 1},
+{"isochronous", optional_argument, &isochronous, 1},
+{"sum-only", no_argument, &sumonly, 1},
+{"local-only", optional_argument, &so_dontroute, 1},
+{"near-congestion", optional_argument, &nearcongest, 1},
+{"permit-key", optional_argument, &permitkey, 1},
+{"permit-key-timeout", required_argument, &permitkeytimeout, 1},
+{"burst-size", required_argument, &burstsize, 1},
+{"burst-period", required_argument, &burstperiodic, 1},
+{"tos-override", required_argument, &overridetos, 1},
+{"tcp-rx-window-clamp", required_argument, &rxwinclamp, 1},
+{"tcp-quickack", no_argument, &tcpquickack, 1},
+{"tcp-write-prefetch", required_argument, &txnotsentlowwater, 1}, // see doc/DESIGN_NOTES
+{"tcp-write-times", no_argument, &tcpwritetimes, 1},
+{"tap-dev", optional_argument, &tapif, 1},
+{"tun-dev", optional_argument, &tunif, 1},
+{"working-load", optional_argument, &workingload, 1},
+{"utc", no_argument, &utctimes, 1},
+{"NUM_REPORT_STRUCTS", required_argument, &numreportstructs, 1},
+#ifdef WIN32
+{"reverse", no_argument, &reversetest, 1},
+#endif
+{0, 0, 0, 0}
+};
+
+#define ENV_OPTIONS()
+
+const struct option env_options[] =
+{
+{"IPERF_IPV6_DOMAIN", no_argument, NULL, 'V'},
+{"IPERF_SINGLECLIENT", no_argument, NULL, '1'},
+{"IPERF_BANDWIDTH", required_argument, NULL, 'b'},
+{"IPERF_CLIENT", required_argument, NULL, 'c'},
+{"IPERF_DUALTEST", no_argument, NULL, 'd'},
+{"IPERF_ENHANCEDREPORTS", no_argument, NULL, 'e'},
+{"IPERF_FORMAT", required_argument, NULL, 'f'},
+// skip help
+{"IPERF_INTERVAL", required_argument, NULL, 'i'},
+{"IPERF_LEN", required_argument, NULL, 'l'},
+{"IPERF_PRINT_MSS", no_argument, NULL, 'm'},
+{"IPERF_NUM", required_argument, NULL, 'n'},
+{"IPERF_PORT", required_argument, NULL, 'p'},
+{"IPERF_TRADEOFF", no_argument, NULL, 'r'},
+{"IPERF_SERVER", no_argument, NULL, 's'},
+{"IPERF_TIME", required_argument, NULL, 't'},
+{"IPERF_UDP", no_argument, NULL, 'u'},
+// skip version
+{"TCP_WINDOW_SIZE", required_argument, NULL, 'w'},
+{"IPERF_REPORTEXCLUDE", required_argument, NULL, 'x'},
+{"IPERF_REPORTSTYLE",required_argument, NULL, 'y'},
+
+// more esoteric options
+{"IPERF_BIND", required_argument, NULL, 'B'},
+{"IPERF_COMPAT", no_argument, NULL, 'C'},
+{"IPERF_DAEMON", no_argument, NULL, 'D'},
+{"IPERF_FILE_INPUT", required_argument, NULL, 'F'},
+{"IPERF_STDIN_INPUT", no_argument, NULL, 'I'},
+{"IPERF_MSS", required_argument, NULL, 'M'},
+{"IPERF_NODELAY", no_argument, NULL, 'N'},
+{"IPERF_LISTENPORT", required_argument, NULL, 'L'},
+{"IPERF_PARALLEL", required_argument, NULL, 'P'},
+{"IPERF_TOS", required_argument, NULL, 'S'},
+{"IPERF_TTL", required_argument, NULL, 'T'},
+{"IPERF_SINGLE_UDP", no_argument, NULL, 'U'},
+{"IPERF_SUGGEST_WIN_SIZE", required_argument, NULL, 'W'},
+{"IPERF_PEER_DETECT", no_argument, NULL, 'X'},
+{"IPERF_CONGESTION_CONTROL", required_argument, NULL, 'Z'},
+{0, 0, 0, 0}
+};
+
+#define SHORT_OPTIONS()
+
+const char short_options[] = "146b:c:def:hi:l:mn:o:p:rst:uvw:x:y:zAB:CDF:H:IL:M:NP:RS:T:UVWXZ:";
+
+/* -------------------------------------------------------------------
+ * defaults
+ * ------------------------------------------------------------------- */
+#define DEFAULTS()
+
+const long kDefault_UDPRate = 1024 * 1024; // -u if set, 1 Mbit/sec
+const int kDefault_TCPBufLen = 128 * 1024; // TCP default read/write size
+const int kDefault_BBTCPBufLen = 100; // default bounce-back size in bytes
+
+
+/* -------------------------------------------------------------------
+ * Initialize all settings to defaults.
+ * ------------------------------------------------------------------- */
+void Settings_Initialize (struct thread_Settings *main) {
+ // Everything defaults to zero or NULL with
+ // this memset. Only need to set non-zero values
+ // below.
+ memset(main, 0, sizeof(struct thread_Settings));
+ main->mSock = INVALID_SOCKET;
+ main->mReportMode = kReport_Default;
+ // option, defaults
+ main->flags = FLAG_MODETIME | FLAG_STDOUT; // Default time and stdout
+ main->flags_extend = 0x0; // Default all extend flags to off
+ main->flags_extend2 = 0x0; // Default all extend flags to off
+ //main->mAppRate = 0; // -b, offered (or rate limited) load (both UDP and TCP)
+ main->mAppRateUnits = kRate_BW;
+ //main->mHost = NULL; // -c, none, required for client
+ main->mMode = kTest_Normal; // -d, mMode == kTest_DualTest
+ main->mFormat = 'a'; // -f, adaptive bits
+ // skip help // -h,
+ //main->mBufLenSet = false; // -l,
+ main->mBufLen = kDefault_TCPBufLen; // -l, Default to TCP read/write size
+ //main->mInterval = 0; // -i, ie. no periodic bw reports
+ //main->mPrintMSS = false; // -m, don't print MSS
+ // mAmount is time also // -n, N/A
+ //main->mOutputFileName = NULL; // -o, filename
+ main->mPort = 5001; // -p, ttcp port
+ main->mBindPort = 0; // -B, default port for bind
+ // mMode = kTest_Normal; // -r, mMode == kTest_TradeOff
+ main->mThreadMode = kMode_Unknown; // -s, or -c, none
+ main->mAmount = 1000; // -t, 10 seconds, units is 10 ms
+ main->mIntervalMode = kInterval_None;// -i none, time, packets, or bursts
+ // skip version // -v,
+ //main->mTCPWin = 0; // -w, ie. don't set window
+
+ // more esoteric options
+ //main->mLocalhost = NULL; // -B, none
+ //main->mCompat = false; // -C, run in Compatibility mode
+ //main->mDaemon = false; // -D, run as a daemon
+ //main->mFileInput = false; // -F,
+ //main->mFileName = NULL; // -F, filename
+ //main->mStdin = false; // -I, default not stdin
+ //main->mListenPort = 0; // -L, listen port
+ //main->mMSS = 0; // -M, ie. don't set MSS
+ //main->mNodelay = false; // -N, don't set nodelay
+ //main->mThreads = 0; // -P,
+ //main->mRemoveService = false; // -R,
+ //main->mTOS = 0; // -S, ie. don't set type of service
+ main->mTTL = -1; // -T, link-local TTL
+ //main->mDomain = kMode_IPv4; // -V,
+ //main->mSuggestWin = false; // -W, Suggest the window size.
+ main->mListenerTimeout = -1; //
+ main->mKeyCheck = true;
+#if defined(HAVE_DECL_SO_DONTROUTE) && defined(HAVE_DEFAULT_DONTROUTE_ON)
+ setDontRoute(main);
+#endif
+ main->mFPS = 1;
+} // end Settings
+
+void Settings_Copy (struct thread_Settings *from, struct thread_Settings **into, int copyall) {
+ *into = new struct thread_Settings;
+ memset(*into, 0, sizeof(struct thread_Settings));
+ memcpy(*into, from, sizeof(struct thread_Settings));
+ (*into)->mSumReport = NULL;
+ (*into)->mTransferIDStr = NULL;
+
+#ifdef HAVE_THREAD_DEBUG
+ thread_debug("Copy thread settings (malloc) from/to=%p/%p report/sum/fullduplex %p/%p/%p", \
+ (void *)from, (void *)*into, (void *)(*into)->reporthdr, (void *)(*into)->mSumReport, (void *)(*into)->mFullDuplexReport);
+#endif
+ // Some settings don't need to be copied and will confuse things. Don't copy them unless copyall is set
+ if (copyall) {
+ // Don't allocate memory for these if this is a reverse client
+ if (from->mHost != NULL) {
+ (*into)->mHost = new char[strlen(from->mHost) + 1];
+ strcpy((*into)->mHost, from->mHost);
+ }
+ if (from->mOutputFileName != NULL) {
+ (*into)->mOutputFileName = new char[strlen(from->mOutputFileName) + 1];
+ strcpy((*into)->mOutputFileName, from->mOutputFileName);
+ }
+ if (from->mLocalhost != NULL) {
+ (*into)->mLocalhost = new char[strlen(from->mLocalhost) + 1];
+ strcpy((*into)->mLocalhost, from->mLocalhost);
+ }
+ if (from->mFileName != NULL) {
+ (*into)->mFileName = new char[strlen(from->mFileName) + 1];
+ strcpy((*into)->mFileName, from->mFileName);
+ }
+ if (from->mHistogramStr != NULL) {
+ (*into)->mHistogramStr = new char[strlen(from->mHistogramStr) + 1];
+ strcpy((*into)->mHistogramStr, from->mHistogramStr);
+ }
+ if (from->mSSMMulticastStr != NULL) {
+ (*into)->mSSMMulticastStr = new char[strlen(from->mSSMMulticastStr) + 1];
+ strcpy((*into)->mSSMMulticastStr, from->mSSMMulticastStr);
+ }
+ if (from->mIfrname != NULL) {
+ (*into)->mIfrname = static_cast<char *>(calloc(strlen(from->mIfrname) + 1, sizeof(char)));
+ strcpy((*into)->mIfrname, from->mIfrname);
+ }
+ if (from->mIfrnametx != NULL) {
+ (*into)->mIfrnametx = static_cast<char *>(calloc(strlen(from->mIfrnametx) + 1, sizeof(char)));
+ strcpy((*into)->mIfrnametx, from->mIfrnametx);
+ }
+ if (from->mIsochronousStr != NULL) {
+ (*into)->mIsochronousStr = new char[strlen(from->mIsochronousStr) + 1];
+ strcpy((*into)->mIsochronousStr, from->mIsochronousStr);
+ }
+ if (from->mHideHost != NULL) {
+ (*into)->mHideHost = new char[strlen(from->mHideHost) + 1];
+ strcpy((*into)->mHideHost, from->mHideHost);
+ }
+ if (from->mCongestion != NULL) {
+ (*into)->mCongestion = new char[strlen(from->mCongestion) + 1];
+ strcpy((*into)->mCongestion, from->mCongestion);
+ }
+ } else {
+ (*into)->mHost = NULL;
+ (*into)->mOutputFileName = NULL;
+ (*into)->mLocalhost = NULL;
+ (*into)->mFileName = NULL;
+ (*into)->mHistogramStr = NULL;
+ (*into)->mSSMMulticastStr = NULL;
+ (*into)->mIfrname = NULL;
+ (*into)->mIfrnametx = NULL;
+ (*into)->mIsochronousStr = NULL;
+ (*into)->mCongestion = NULL;
+ // apply the server side congestion setting to reverse clients
+ if (from->mIsochronousStr != NULL) {
+ (*into)->mIsochronousStr = new char[strlen(from->mIsochronousStr) + 1];
+ strcpy((*into)->mIsochronousStr, from->mIsochronousStr);
+ }
+ }
+
+ (*into)->txstart_epoch = from->txstart_epoch;
+ (*into)->mSumReport = from->mSumReport;
+ (*into)->mFullDuplexReport = from->mFullDuplexReport;
+
+ // Zero out certain entries
+ (*into)->mTID = thread_zeroid();
+ (*into)->runNext = NULL;
+ (*into)->runNow = NULL;
+#if defined(HAVE_LINUX_FILTER_H) && defined(HAVE_AF_PACKET)
+ (*into)->mSockDrop = INVALID_SOCKET;
+#endif
+ int mbuflen = (from->mBufLen > MINMBUFALLOCSIZE) ? from->mBufLen : MINMBUFALLOCSIZE; // defined in payloads.h
+#if (((HAVE_TUNTAP_TUN) || (HAVE_TUNTAP_TAP)) && (AF_PACKET))
+ mbuflen += TAPBYTESSLOP;
+#endif
+ (*into)->mBuf = new char[mbuflen];
+ memset((*into)->mBuf, 0, mbuflen);
+
+#ifdef HAVE_THREAD_DEBUG
+ thread_debug("Copy Settings: MBUF malloc %d bytes (%p)", mbuflen, (void *) (*into)->mBuf);
+#endif
+ Condition_Initialize(&(*into)->awake_me);
+ // default copied settings to no reporter reporting
+ unsetReport((*into));
+}
+
+/* -------------------------------------------------------------------
+ * Delete memory: Does not clean up open file pointers or ptr_parents
+ * ------------------------------------------------------------------- */
+
+void Settings_Destroy (struct thread_Settings *mSettings) {
+#if HAVE_THREAD_DEBUG
+ thread_debug("Free thread settings=%p", mSettings);
+#endif
+ if (mSettings->tuntapdev)
+ close(mSettings->tuntapdev);
+ Condition_Destroy(&mSettings->awake_me);
+ DELETE_ARRAY(mSettings->mHost);
+ DELETE_ARRAY(mSettings->mHideHost);
+ DELETE_ARRAY(mSettings->mLocalhost);
+ DELETE_ARRAY(mSettings->mFileName);
+ DELETE_ARRAY(mSettings->mOutputFileName);
+ DELETE_ARRAY(mSettings->mHistogramStr);
+ DELETE_ARRAY(mSettings->mSSMMulticastStr);
+ DELETE_ARRAY(mSettings->mCongestion);
+ FREE_ARRAY(mSettings->mIfrname);
+ FREE_ARRAY(mSettings->mIfrnametx);
+ FREE_ARRAY(mSettings->mTransferIDStr);
+ DELETE_ARRAY(mSettings->mIsochronousStr);
+ DELETE_ARRAY(mSettings->mBuf);
+ DELETE_PTR(mSettings);
+} // end ~Settings
+
+/* -------------------------------------------------------------------
+ * Parses settings from user's environment variables.
+ * ------------------------------------------------------------------- */
+void Settings_ParseEnvironment (struct thread_Settings *mSettings) {
+ char *theVariable;
+
+ int i = 0;
+ while (env_options[i].name != NULL) {
+ theVariable = getenv(env_options[i].name);
+ if (theVariable != NULL) {
+ Settings_Interpret(env_options[i].val, theVariable, mSettings);
+ }
+ i++;
+ }
+} // end ParseEnvironment
+
+/* -------------------------------------------------------------------
+ * Parse settings from app's command line.
+ * ------------------------------------------------------------------- */
+
+void Settings_ParseCommandLine (int argc, char **argv, struct thread_Settings *mSettings) {
+ int option;
+ gnu_opterr = 1; // Fail on an unrecognized command line option
+ while ((option =
+ gnu_getopt_long(argc, argv, short_options,
+ long_options, NULL)) != EOF) {
+ Settings_Interpret(option, gnu_optarg, mSettings);
+ }
+
+ for (int i = gnu_optind; i < argc; i++) {
+ fprintf(stderr, "%s: ignoring extra argument -- %s\n", argv[0], argv[i]);
+ }
+ // Determine the modal or compound settings now that the full command line has been parsed
+ Settings_ModalOptions(mSettings);
+
+} // end ParseCommandLine
+
+/* -------------------------------------------------------------------
+ * Interpret individual options, either from the command line
+ * or from environment variables.
+ * ------------------------------------------------------------------- */
+
+void Settings_Interpret (char option, const char *optarg, struct thread_Settings *mExtSettings) {
+ char *results;
+ switch (option) {
+ case '1': // Single Client
+ setSingleClient(mExtSettings);
+ break;
+
+ case '4': // v4 only
+ setIPV4(mExtSettings);
+ break;
+
+ case '6': // v6 only
+ setIPV6(mExtSettings);
+ break;
+
+ case 'b': // UDP bandwidth
+ {
+ char *tmp= new char [strlen(optarg) + 1];
+ strcpy(tmp, optarg);
+ // scan for PPS units, just look for 'p' as that's good enough
+ if ((((results = strtok(tmp, "p")) != NULL) && strcmp(results,optarg) != 0) \
+ || (((results = strtok(tmp, "P")) != NULL) && strcmp(results,optarg) != 0)) {
+ mExtSettings->mAppRateUnits = kRate_PPS;
+ mExtSettings->mAppRate = byte_atoi(results);
+ } else {
+ mExtSettings->mAppRateUnits = kRate_BW;
+ mExtSettings->mAppRate = byte_atoi(optarg);
+ if (((results = strtok(tmp, ",")) != NULL) && strcmp(results,optarg) != 0) {
+ setVaryLoad(mExtSettings);
+ mExtSettings->mVariance = byte_atoi(optarg);
+ }
+ }
+ delete [] tmp;
+ }
+ setBWSet(mExtSettings);
+ break;
+ case 'c': // client mode w/ server host to connect to
+ mExtSettings->mHost = new char[strlen(optarg) + 1 ];
+ strcpy(mExtSettings->mHost, optarg);
+
+ if (mExtSettings->mThreadMode == kMode_Unknown) {
+ mExtSettings->mThreadMode = kMode_Client;
+ mExtSettings->mThreads = 1;
+ }
+ break;
+
+ case 'd': // Dual-test Mode
+ if (mExtSettings->mThreadMode != kMode_Client) {
+ fprintf(stderr, warn_invalid_server_option, option);
+ break;
+ }
+ if (isCompat(mExtSettings)) {
+ fprintf(stderr, warn_invalid_compatibility_option, option);
+ }
+#ifdef HAVE_THREAD
+ mExtSettings->mMode = kTest_DualTest;
+#else
+ fprintf(stderr, warn_invalid_single_threaded, option);
+ mExtSettings->mMode = kTest_TradeOff;
+#endif
+ break;
+ case 'e': // Use enhanced reports
+ setEnhanced(mExtSettings);
+ break;
+ case 'f': // format to print in
+ mExtSettings->mFormat = (*optarg);
+ break;
+
+ case 'h': // print help and exit
+ fprintf(stdout, "%s", usage_long1);
+ fprintf(stdout, "%s", usage_long2);
+ exit(0);
+ break;
+
+ case 'i': // specify interval between periodic bw reports
+ {
+ char framechar;
+ char *tmp= new char [strlen(optarg) + 1];
+ strcpy(tmp, optarg);
+ // scan for frames as units
+ if ((sscanf(optarg,"%c", &framechar)) && ((framechar == 'f') || (framechar == 'F'))) {
+ mExtSettings->mIntervalMode = kInterval_Frames;
+ setEnhanced(mExtSettings);
+ setFrameInterval(mExtSettings);
+ } else {
+ char *end;
+ strcpy(tmp, optarg);
+ double itime = strtof(optarg, &end);
+ if (*end != '\0') {
+ fprintf (stderr, "Invalid value of '%s' for -i interval\n", optarg);
+ exit(1);
+ }
+ if (itime > (UINT_MAX / 1e6)) {
+ fprintf (stderr, "Too large value of '%s' for -i interval, max is %f\n", optarg, (UINT_MAX / 1e6));
+ exit(1);
+ }
+ mExtSettings->mInterval = static_cast<unsigned int>(ceil(itime * 1e6));
+ mExtSettings->mIntervalMode = kInterval_Time;
+ if (mExtSettings->mInterval <= 0) {
+ mExtSettings->mInterval = -1;
+ } else if (mExtSettings->mInterval < SMALLEST_INTERVAL) {
+ mExtSettings->mInterval = SMALLEST_INTERVAL;
+ fprintf (stderr, report_interval_small, (double) mExtSettings->mInterval / 1e3);
+ }
+ }
+ delete [] tmp;
+ }
+ break;
+
+ case 'l': // length of each buffer
+ mExtSettings->mBufLen = byte_atoi(optarg);
+ setBuflenSet(mExtSettings);
+ break;
+
+ case 'm': // print TCP MSS
+#if HAVE_DECL_TCP_MAXSEG
+ setPrintMSS(mExtSettings);
+#endif
+ break;
+
+ case 'n': // bytes of data
+ // amount mode (instead of time mode)
+ unsetModeTime(mExtSettings);
+ mExtSettings->mAmount = byte_atoi(optarg);
+ if (!(mExtSettings->mAmount > 0)) {
+ fprintf (stderr, "Invalid value for -n amount of '%s'\n", optarg);
+ exit(1);
+ }
+ break;
+
+ case 'o' : // output the report and other messages into the file
+ unsetSTDOUT(mExtSettings);
+ mExtSettings->mOutputFileName = new char[strlen(optarg)+1];
+ strcpy(mExtSettings->mOutputFileName, optarg);
+ break;
+
+ case 'p': // server port
+ {
+ char *tmp= new char [strlen(optarg) + 1];
+ strcpy(tmp, optarg);
+ if ((results = strtok(tmp, "-")) != NULL) {
+ mExtSettings->mPort = atoi(results);
+ if (strcmp(results,optarg)) {
+ mExtSettings->mPortLast = atoi(strtok(NULL, "-"));
+ setIncrDstPort(mExtSettings);
+ }
+ }
+ delete [] tmp;
+ break;
+ }
+ case 'r': // test mode tradeoff
+ if (mExtSettings->mThreadMode != kMode_Client) {
+ fprintf(stderr, warn_invalid_server_option, option);
+ break;
+ }
+ if (isCompat(mExtSettings)) {
+ fprintf(stderr, warn_invalid_compatibility_option, option);
+ }
+ mExtSettings->mMode = kTest_TradeOff;
+ break;
+
+ case 's': // server mode
+ if (mExtSettings->mThreadMode != kMode_Unknown) {
+ fprintf(stderr, warn_invalid_client_option, option);
+ break;
+ }
+
+ mExtSettings->mThreadMode = kMode_Listener;
+ break;
+
+ case 't': // seconds to run the client, server, listener
+ if (!optarg) {
+ fprintf(stderr, "ERROR: option -t requires value\n");
+ exit(1);
+ }
+ {
+ double val;
+#if HAVE_STRTOD
+ char *end;
+ errno = 0;
+ val = strtod(optarg, &end);
+ if (errno || (*end != '\0')) {
+ fprintf(stderr, "ERROR: -t value of '%s' not recognized\n", optarg);
+ exit(1);
+ }
+#else
+ val = atof(optarg);
+#endif
+ if (val > 0.0)
+ mExtSettings->mAmount = static_cast<size_t>(val * 100.0);
+ else
+ infinitetime = 1;
+ // time mode (instead of amount mode), units is 10 ms
+ setModeTime(mExtSettings);
+ setServerModeTime(mExtSettings);
+ }
+ break;
+ case 'u': // UDP instead of TCP
+ setUDP(mExtSettings);
+ break;
+
+ case 'v': // print version and exit
+ fprintf(stdout, "%s", version);
+ exit(0);
+ break;
+
+ case 'w': // TCP window size (socket buffer size)
+ mExtSettings->mTCPWin = byte_atoi(optarg);
+ if (mExtSettings->mTCPWin < 2048) {
+ fprintf(stderr, warn_window_small, mExtSettings->mTCPWin);
+ }
+ break;
+
+ case 'x': // Limit Reports
+ while (*optarg != '\0') {
+ switch (*optarg) {
+ case 's':
+ case 'S':
+ setNoSettReport(mExtSettings);
+ break;
+ case 'c':
+ case 'C':
+ setNoConnReport(mExtSettings);
+ break;
+ case 'd':
+ case 'D':
+ setNoDataReport(mExtSettings);
+ break;
+ case 'v':
+ case 'V':
+ setNoServReport(mExtSettings);
+ break;
+ case 'm':
+ case 'M':
+ setNoMultReport(mExtSettings);
+ break;
+ default:
+ fprintf(stderr, warn_invalid_report, *optarg);
+ }
+ optarg++;
+ }
+ break;
+#if HAVE_SCHED_SETSCHEDULER
+ case 'z': // Use realtime scheduling
+ setRealtime(mExtSettings);
+ break;
+#endif
+
+ case 'y': // Reporting Style
+ switch (*optarg) {
+ case 'c':
+ case 'C':
+ mExtSettings->mReportMode = kReport_CSV;
+ setNoSettReport(mExtSettings);
+ setNoConnReport(mExtSettings);
+ break;
+ default:
+ fprintf(stderr, warn_invalid_report_style, optarg);
+ }
+ break;
+
+ // more esoteric options
+
+ case 'B': // specify bind address
+ if (mExtSettings->mLocalhost == NULL) {
+ mExtSettings->mLocalhost = new char[strlen(optarg) + 1 ];
+ strcpy(mExtSettings->mLocalhost, optarg);
+ }
+ break;
+
+ case 'C': // Run in Compatibility Mode, i.e. no intial nor final header messaging
+ setCompat(mExtSettings);
+ if (mExtSettings->mMode != kTest_Normal) {
+ fprintf(stderr, warn_invalid_compatibility_option,
+ (mExtSettings->mMode == kTest_DualTest ?
+ 'd' : 'r'));
+ mExtSettings->mMode = kTest_Normal;
+ }
+ break;
+
+ case 'D': // Run as a daemon
+ setDaemon(mExtSettings);
+ break;
+
+ case 'F' : // Get the input for the data stream from a file
+ if (mExtSettings->mThreadMode != kMode_Client) {
+ fprintf(stderr, warn_invalid_server_option, option);
+ break;
+ }
+
+ setFileInput(mExtSettings);
+ mExtSettings->mFileName = new char[strlen(optarg)+1];
+ strcpy(mExtSettings->mFileName, optarg);
+ break;
+
+ case 'H' : // Get the SSM host (or Source per the S,G)
+ if (mExtSettings->mThreadMode == kMode_Client) {
+ fprintf(stderr, warn_invalid_client_option, option);
+ break;
+ }
+ mExtSettings->mSSMMulticastStr = new char[strlen(optarg)+1];
+ strcpy(mExtSettings->mSSMMulticastStr, optarg);
+ setSSMMulticast(mExtSettings);
+ break;
+
+ case 'I' : // Set the stdin as the input source
+ if (mExtSettings->mThreadMode != kMode_Client) {
+ fprintf(stderr, warn_invalid_server_option, option);
+ break;
+ }
+
+ setFileInput(mExtSettings);
+ setSTDIN(mExtSettings);
+ mExtSettings->mFileName = new char[strlen("<stdin>")+1];
+ strcpy(mExtSettings->mFileName,"<stdin>");
+ break;
+
+ case 'L': // Listen Port (bidirectional testing client-side)
+ if (mExtSettings->mThreadMode != kMode_Client) {
+ fprintf(stderr, warn_invalid_server_option, option);
+ break;
+ }
+ mExtSettings->mListenPort = atoi(optarg);
+ break;
+
+ case 'M': // specify TCP MSS (maximum segment size)
+#if HAVE_DECL_TCP_MAXSEG
+ mExtSettings->mMSS = byte_atoi(optarg);
+ setTCPMSS(mExtSettings);
+ setPrintMSS(mExtSettings);
+#endif
+ break;
+
+ case 'N': // specify TCP nodelay option (disable Jacobson's Algorithm)
+ setNoDelay(mExtSettings);
+ break;
+
+ case 'P': // number of client threads
+#ifdef HAVE_THREAD
+ mExtSettings->mThreads = atoi(optarg);
+#else
+ if (mExtSettings->mThreadMode != kMode_Server) {
+ fprintf(stderr, warn_invalid_single_threaded, option);
+ } else {
+ mExtSettings->mThreads = atoi(optarg);
+ }
+#endif
+ break;
+#ifdef WIN32
+ case 'R':
+ setRemoveService(mExtSettings);
+ break;
+#else
+ case 'R':
+ setReverse(mExtSettings);
+ break;
+#endif
+ case 'S': // IP type-of-service
+ // TODO use a function that understands base-2
+ // the zero base here allows the user to specify
+ // "0x#" hex, "0#" octal, and "#" decimal numbers
+ if ((mExtSettings->mTOS = parse_ipqos(optarg)) == -1) {
+ fprintf(stderr, "Invalid --tos value of %s\n", optarg);
+ mExtSettings->mTOS = 0;
+ }
+ break;
+
+ case 'T': // time-to-live for both unicast and multicast
+ mExtSettings->mTTL = atoi(optarg);
+ break;
+
+ case 'U': // single threaded UDP server
+ setUDP(mExtSettings);
+ setSingleUDP(mExtSettings);
+ setSingleClient(mExtSettings);
+ break;
+
+ case 'V': // IPv6 Domain
+#if HAVE_IPV6
+ setIPV6(mExtSettings);
+#else
+ fprintf(stderr, "The --ipv6_domain (-V) option is not enabled in this build.\n");
+ exit(1);
+#endif
+ break;
+
+ case 'W' :
+ setSuggestWin(mExtSettings);
+ fprintf(stderr, "The -W option is not available in this release\n");
+ break;
+
+ case 'X' :
+ setPeerVerDetect(mExtSettings);
+ break;
+
+ case 'Z':
+#ifdef TCP_CONGESTION
+ setCongestionControl(mExtSettings);
+ mExtSettings->mCongestion = new char[strlen(optarg)+1];
+ strcpy(mExtSettings->mCongestion, optarg);
+#else
+ fprintf(stderr, "The -Z option is not available on this operating system\n");
+#endif
+ break;
+
+ case 0:
+ if (incrdstip) {
+ incrdstip = 0;
+ setIncrDstIP(mExtSettings);
+ }
+ if (incrdstport) {
+ incrdstport = 0;
+ setIncrDstPort(mExtSettings);
+ }
+ if (incrsrcip) {
+ incrsrcip = 0;
+ setIncrSrcIP(mExtSettings);
+ }
+ if (incrsrcport) {
+ incrsrcport = 0;
+ setIncrSrcPort(mExtSettings);
+ }
+ if (sumdstip) {
+ sumdstip = 0;
+ setSumServerDstIP(mExtSettings);
+ }
+ if (txstarttime) {
+ long seconds;
+ long usecs;
+ int match = 0;
+ txstarttime = 0;
+ setTxStartTime(mExtSettings);
+ setEnhanced(mExtSettings);
+ match = sscanf(optarg,"%ld.%6ld", &seconds, &usecs);
+ mExtSettings->txstart_epoch.tv_usec = 0;
+ switch (match) {
+ case 2:
+ mExtSettings->txstart_epoch.tv_usec = usecs;
+ case 1:
+ mExtSettings->txstart_epoch.tv_sec = seconds;
+ break;
+ default:
+ unsetTxStartTime(mExtSettings);
+ fprintf(stderr, "WARNING: invalid --txstart-time format\n");
+ }
+ }
+ if (noconnectsync) {
+#ifdef HAVE_THREAD
+ noconnectsync = 0;
+ setNoConnectSync(mExtSettings);
+#else
+ fprintf(stderr, "WARNING: --no-connect-sync requires thread support and not supported\n");
+#endif
+ }
+ if (txholdback) {
+ txholdback = 0;
+ char *end;
+ Timestamp holdbackdelay;
+ double delay = strtof(optarg, &end);
+ if (*end != '\0') {
+ fprintf (stderr, "Invalid value of '%s' for --txdelay-time time\n", optarg);
+ } else {
+ holdbackdelay.set(delay);
+ mExtSettings->txholdback_timer.tv_sec = holdbackdelay.getSecs();
+ mExtSettings->txholdback_timer.tv_usec = (holdbackdelay.getUsecs());
+ setTxHoldback(mExtSettings);
+ }
+ }
+ if (triptime) {
+ triptime = 0;
+ setTripTime(mExtSettings);
+ }
+ if (noudpfin) {
+ noudpfin = 0;
+ setNoUDPfin(mExtSettings);
+ }
+ if (connectonly) {
+ connectonly = 0;
+ setConnectOnly(mExtSettings);
+ unsetNoConnReport(mExtSettings);
+ if (optarg) {
+ mExtSettings->connectonly_count = atoi(optarg);
+ } else {
+ mExtSettings->connectonly_count = -1;
+ }
+ }
+ if (connectretry) {
+ connectretry = 0;
+ mExtSettings->mConnectRetries = atoi(optarg);
+ }
+ if (sumonly) {
+ sumonly = 0;
+ setSumOnly(mExtSettings);
+ }
+ if (so_dontroute) {
+ so_dontroute = 0;
+#if HAVE_DECL_SO_DONTROUTE
+ setDontRoute(mExtSettings);
+ if (optarg) {
+ if (atoi(optarg))
+ setDontRoute(mExtSettings);
+ else
+ unsetDontRoute(mExtSettings);
+ }
+#else
+ fprintf(stderr, "WARNING: The --local-only option is not supported on this platform\n");
+#endif
+ }
+ if (nearcongest) {
+ nearcongest = 0;
+ setNearCongest(mExtSettings);
+ if (optarg && (atof(optarg) >= 0.0)) {
+ mExtSettings->rtt_nearcongest_weight_factor = atof(optarg);
+ } else {
+ mExtSettings->rtt_nearcongest_weight_factor = NEARCONGEST_DEFAULT;
+ }
+ }
+ if (permitkey) {
+ permitkey = 0;
+ if (optarg) {
+ strncpy(mExtSettings->mPermitKey, optarg, MAX_PERMITKEY_LEN);
+ mExtSettings->mPermitKey[MAX_PERMITKEY_LEN] = '\0';
+ } else {
+ mExtSettings->mPermitKey[0] = '\0';
+ }
+ setPermitKey(mExtSettings);
+ }
+ if (permitkeytimeout) {
+ permitkeytimeout = 0;
+ if (atof(optarg) >= 0.0)
+ mExtSettings->mListenerTimeout = static_cast<size_t>(atof(optarg));
+ }
+ if (histogram) {
+ histogram = 0;
+ setHistogram(mExtSettings);
+ setEnhanced(mExtSettings);
+ if (optarg) {
+ mExtSettings->mHistogramStr = new char[strlen(optarg) + 1 ];
+ strcpy(mExtSettings->mHistogramStr, optarg);
+ } else {
+ mExtSettings->mHistogramStr = NULL;
+ }
+ }
+ if (jitter_histogram) {
+ jitter_histogram = 0;
+ setJitterHistogram(mExtSettings);
+ setEnhanced(mExtSettings);
+ if (optarg) {
+ mExtSettings->jitter_binwidth = atoi(optarg);
+ } else {
+ mExtSettings->jitter_binwidth = JITTER_BINWIDTH; // units are usecs
+ }
+ }
+ if (reversetest) {
+ reversetest = 0;
+ setReverse(mExtSettings);
+ }
+ if (fullduplextest) {
+ fullduplextest = 0;
+ setFullDuplex(mExtSettings);
+ }
+ if (overridetos) {
+ overridetos = 0;
+ if ((mExtSettings->mRTOS = parse_ipqos(optarg)) == -1) {
+ fprintf(stderr, "Invalid --tos-overide value of %s\n", optarg);
+ } else {
+ setOverrideTOS(mExtSettings);
+ }
+ }
+ if (fqrate) {
+#if defined(HAVE_DECL_SO_MAX_PACING_RATE)
+ fqrate=0;
+ setFQPacing(mExtSettings);
+ mExtSettings->mFQPacingRate = static_cast<uintmax_t>(bitorbyte_atoi(optarg) / 8);
+#else
+ fprintf(stderr, "WARNING: The --fq-rate option is not supported\n");
+#endif
+ }
+ if (isochronous) {
+ isochronous = 0;
+ setEnhanced(mExtSettings);
+ setIsochronous(mExtSettings);
+ // The following are default values which
+ // may be overwritten during modal parsing
+ mExtSettings->mFPS = 60.0;
+ mExtSettings->mMean = 20000000.0;
+ mExtSettings->mVariance = 0.0;
+ mExtSettings->mBurstIPG = 5e-6;
+ if (optarg) {
+ mExtSettings->mIsochronousStr = new char[strlen(optarg) + 1 ];
+ strcpy(mExtSettings->mIsochronousStr, optarg);
+ }
+ }
+ if (burstipg) {
+ burstipg = 0;
+ setIPG(mExtSettings);
+ char *end;
+ mExtSettings->mBurstIPG = strtof(optarg,&end);
+ if (*end != '\0') {
+ fprintf (stderr, "ERRPORE: Invalid value of '%s' for --ipg\n", optarg);
+ exit(1);
+ }
+ }
+ if (rxwinclamp) {
+ rxwinclamp = 0;
+#if HAVE_DECL_TCP_WINDOW_CLAMP
+ mExtSettings->mClampSize = byte_atoi(optarg);
+ setRxClamp(mExtSettings);
+#else
+ fprintf(stderr, "--tcp-rx-window-clamp not supported on this platform\n");
+#endif
+ }
+ if (tcpwritetimes) {
+ tcpwritetimes = 0;
+ setTcpWriteTimes(mExtSettings);
+ }
+ if (notcpbbquickack) {
+ notcpbbquickack = 0;
+ notcpbbquickack_cliset = 1;
+ }
+ if (tcpquickack) {
+ tcpquickack = 0;
+#if HAVE_DECL_TCP_QUICKACK
+ setTcpQuickAck(mExtSettings);
+#endif
+ }
+ if (utctimes) {
+ setUTC(mExtSettings);
+ }
+ if (workingload) {
+ workingload = 0;
+#ifdef HAVE_THREAD
+ setWorkingLoadUp(mExtSettings);
+ setWorkingLoadDown(mExtSettings);
+ if (optarg) {
+ char *tmp= new char [strlen(optarg) + 1];
+ if (tmp) {
+ strcpy(tmp, optarg);
+ if ((results = strtok(tmp, ",")) != NULL) {
+ if (strcasecmp(results, "up") == 0) {
+ unsetWorkingLoadDown(mExtSettings);
+ } else if (strcasecmp(results, "down") == 0) {
+ unsetWorkingLoadUp(mExtSettings);
+ } else if (strcasecmp(results, "bidir") == 0) {
+ setWorkingLoadUp(mExtSettings);
+ setWorkingLoadDown(mExtSettings);
+ } else {
+ fprintf(stderr, "Unrecoginized value of %s for --working-load, use 'up', 'down' or 'bidir'\n", results);
+ }
+ if ((results = strtok(NULL, ",")) != NULL) {
+ mExtSettings->mWorkingLoadThreads = atoi(results);
+ }
+ delete [] tmp;
+ }
+ }
+ }
+#else
+ fprintf(stderr, "bounceback-congest option requires a platform that supports threads\n");
+#endif
+ }
+ if (bouncebackdelaystart) {
+ bouncebackdelaystart = 0;
+ char *end;
+ Timestamp holdbackdelay;
+ double delay = strtof(optarg, &end);
+ if (*end != '\0') {
+ fprintf (stderr, "Invalid value of '%s' for --bounceback-txdelay time\n", optarg);
+ } else {
+ holdbackdelay.set(delay);
+ mExtSettings->txholdback_timer.tv_sec = holdbackdelay.getSecs();
+ mExtSettings->txholdback_timer.tv_usec = (holdbackdelay.getUsecs());
+ setTxHoldback(mExtSettings);
+ }
+ }
+ if (txnotsentlowwater) {
+ txnotsentlowwater = 0;
+#if HAVE_DECL_TCP_NOTSENT_LOWAT
+ mExtSettings->mWritePrefetch = byte_atoi(optarg);
+ setWritePrefetch(mExtSettings);
+ setEnhanced(mExtSettings);
+#else
+ fprintf(stderr, "--tcp-write-prefetch not supported on this platform\n");
+#endif
+ }
+ if (burstperiodic) {
+ burstperiodic = 0;
+ setPeriodicBurst(mExtSettings);
+ if (optarg && (atof(optarg) > 1e-5)) { // limit to 10 usecs
+ mExtSettings->mFPS = 1.0/atof(optarg);
+ } else {
+ if (atof(optarg) != 0)
+ fprintf(stderr, "WARN: burst-period too small, must be greater than 10 usecs\n");
+ unsetPeriodicBurst(mExtSettings);
+ }
+ }
+ if (burstsize) {
+ burstsize = 0;
+ setPeriodicBurst(mExtSettings);
+ if (optarg) {
+ mExtSettings->mBurstSize = byte_atoi(optarg);
+ }
+ }
+ if (numreportstructs) {
+ numreportstructs = 0;
+ mExtSettings->numreportstructs = byte_atoi(optarg);
+ }
+ if (tapif) {
+ tapif = 0;
+#if HAVE_TUNTAP_TAP
+ if (optarg) {
+ mExtSettings->mIfrname = static_cast<char *>(calloc(strlen(optarg) + 1, sizeof(char)));
+ strcpy(mExtSettings->mIfrname, optarg);
+ }
+ setTapDev(mExtSettings);
+ setEnhanced(mExtSettings);
+ setL2LengthCheck(mExtSettings);
+#else
+ fprintf(stderr, "ERROR: tap devices not supported\n");
+ exit(1);
+#endif
+ }
+ if (tunif) {
+ tunif = 0;
+ fprintf(stderr, "ERROR: tun devices not yet supported\n");
+ exit(1);
+#if HAVE_TUNTAP_TUN
+ if (optarg) {
+ mExtSettings->mIfrname = static_cast<char *>(calloc(strlen(optarg) + 1, sizeof(char)));
+ strcpy(mExtSettings->mIfrname, optarg);
+ }
+ setTunDev(mExtSettings);
+ setEnhanced(mExtSettings);
+#endif
+ }
+ if (hideips) {
+ hideips = 0;
+ setHideIPs(mExtSettings);
+ }
+ if (bounceback) {
+ bounceback = 0;
+ setBounceBack(mExtSettings);
+ setNoDelay(mExtSettings);
+ setEnhanced(mExtSettings);
+ if (optarg) {
+ mExtSettings->mBounceBackBurst = atoi(optarg);
+ if (mExtSettings->mBounceBackBurst <= 0) {
+ fprintf(stderr, "WARN: invalid bounceback value, setting it to 10\n");
+ mExtSettings->mBounceBackBurst = 10;
+ }
+ } else {
+ mExtSettings->mBounceBackBurst = -1;
+ }
+ }
+ if (bouncebackhold) {
+ bouncebackhold = 0;
+ if (optarg)
+ //cli units is ms, working units is us
+ mExtSettings->mBounceBackHold = int(atof(optarg) * 1e3);
+ else
+ mExtSettings->mBounceBackHold = 0;
+ }
+ if (bouncebackperiod) {
+ bouncebackperiod = 0;
+ setPeriodicBurst(mExtSettings);
+ if (optarg) {
+ float value = atof(optarg);
+ if (value > 1e-5) { // limit to 10 usecs
+ mExtSettings->mFPS = 1.0/atof(optarg);
+ } else {
+ mExtSettings->mFPS = -1;
+ }
+ }
+ }
+ break;
+ default: // ignore unknown
+ break;
+ }
+} // end Interpret
+
+
+static void generate_permit_key (struct thread_Settings *mExtSettings) {
+ Timestamp now;
+ mExtSettings->mPermitKeyTime.tv_sec = now.getSecs();
+ mExtSettings->mPermitKeyTime.tv_usec = now.getUsecs();
+ int usecs = mExtSettings->mPermitKeyTime.tv_usec;
+ int timestrlength = snprintf(NULL, 0, "%ld.%06d-", static_cast<long>(mExtSettings->mPermitKeyTime.tv_sec), usecs);
+ snprintf(mExtSettings->mPermitKey, (timestrlength+1), "%ld.%06d-", static_cast<long>(mExtSettings->mPermitKeyTime.tv_sec), usecs);
+ srand(static_cast<unsigned int>(time(NULL)));
+ int index;
+ char characters[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
+ for(index = timestrlength; index < (DEFAULT_PERMITKEY_LEN + timestrlength); index++) {
+ int len = snprintf(mExtSettings->mPermitKey + index, sizeof(char) + 1, "%c", \
+ characters[rand() % (static_cast<int>(sizeof(characters)) - 1)]);
+ FAIL_errno ((len < 0), "permit-key gen", mExtSettings);
+ }
+ mExtSettings->mPermitKey[DEFAULT_PERMITKEY_LEN + timestrlength] = '\0';
+ if (strlen(mExtSettings->mPermitKey) > MAX_PERMITKEY_LEN) {
+ fprintf(stderr, "ERROR: permit key too long\n");
+ exit(1);
+ }
+}
+
+static void strip_v6_brackets (char *v6addr) {
+ char * results;
+ if (v6addr && (*v6addr == '[') && ((results = strtok(v6addr, "]")) != NULL)) {
+ int len = strlen(v6addr);
+ for (int jx = 0; jx < len; jx++) {
+ v6addr[jx]= v6addr[jx + 1];
+ }
+ }
+}
+
+static char * isv6_bracketed_port (char *v6addr) {
+ char *results = NULL;
+ if (v6addr && (*v6addr == '[') && ((results = strtok(v6addr, "]")) != NULL)) {
+ results = strtok(NULL, ":");
+ strip_v6_brackets(v6addr);
+ return results;
+ }
+ return NULL;
+}
+static char * isv4_port (char *v4addr) {
+ char *results = NULL;
+ if (((results = strtok(v4addr, ":")) != NULL) && ((results = strtok(NULL, ":")) != NULL)) {
+ return results;
+ }
+ return NULL;
+}
+
+
+// The commmand line options are position independent and hence some settings become "modal"
+// i.e. two passes are required to get all the final settings correct.
+// For example, -V indicates use IPv6 and -u indicates use UDP, and the default socket
+// read/write (UDP payload) size is different for ipv4 and ipv6.
+// So in the Settings_Interpret pass there is no guarantee to know all three of (-u and -V and not -l)
+// while parsing them individually.
+//
+// Since Settings_Interpret() will set all the *individual* options and flags
+// then the below code (per the example UDP, v4 or v6, and not -l) can set final
+// values, e.g. a correct default mBufLen. Other examples that need this are multicast
+// socket or not,-B local bind port parsing, and when to use the default UDP offered load
+//
+// Also apply the bail out or exit conditions if the user requested mutually exclusive
+// or incompatabile options
+void Settings_ModalOptions (struct thread_Settings *mExtSettings) {
+ char *results;
+ // Handle default read/write sizes based on v4, v6, UDP or TCP
+ if (!isBuflenSet(mExtSettings)) {
+ if (isUDP(mExtSettings)) {
+ if (isIPV6(mExtSettings) && mExtSettings->mThreadMode == kMode_Client) {
+ mExtSettings->mBufLen = kDefault_UDPBufLenV6;
+ } else {
+ mExtSettings->mBufLen = kDefault_UDPBufLen;
+ }
+ } else {
+ if (isBounceBack(mExtSettings))
+ mExtSettings->mBufLen = kDefault_BBTCPBufLen;
+ else
+ mExtSettings->mBufLen = kDefault_TCPBufLen;
+ }
+ }
+ if (!mExtSettings->mPortLast)
+ mExtSettings->mPortLast = mExtSettings->mPort;
+
+ // Handle default UDP offered load (TCP will be max, i.e. no read() or write() rate limiting)
+ if (!isBWSet(mExtSettings) && isUDP(mExtSettings)) {
+ mExtSettings->mAppRate = kDefault_UDPRate;
+ }
+ if (isTripTime(mExtSettings) && (isReverse(mExtSettings) || \
+ isFullDuplex(mExtSettings) || \
+ (mExtSettings->mMode != kTest_Normal))) {
+ setEnhanced(mExtSettings);
+ }
+ // Warnings
+ if (mExtSettings->mThreadMode == kMode_Client) {
+ if (isModeTime(mExtSettings) && infinitetime) {
+ unsetModeTime(mExtSettings);
+ setModeInfinite(mExtSettings);
+ fprintf(stderr, "WARNING: client will send traffic forever or until an external signal (e.g. SIGINT or SIGTERM) occurs to stop it\n");
+ }
+ if (isFullDuplex(mExtSettings) && isCongestionControl(mExtSettings)) {
+ fprintf(stderr, "WARNING: tcp congestion control will only be applied on transmit traffic, use -Z on the server\n");
+ }
+ }
+ if (isHideIPs(mExtSettings)) {
+ char hide_string[] = "(**hidden**)";
+ mExtSettings->mHideHost = new char[strlen(hide_string) + 1];
+ if (mExtSettings->mHideHost)
+ strcpy(mExtSettings->mHideHost, &hide_string[0]);
+ }
+
+ // Bail outs
+ bool bail = false;
+ // compat mode doesn't support these test settings
+ int compat_nosupport = (isReverse(mExtSettings) | isFullDuplex(mExtSettings) | isTripTime(mExtSettings) | isVaryLoad(mExtSettings) \
+ | isHistogram(mExtSettings) | isIsochronous(mExtSettings) \
+ | isEnhanced(mExtSettings) | (mExtSettings->mMode != kTest_Normal));
+ if (isCompat(mExtSettings) && compat_nosupport) {
+ fprintf(stderr, "ERROR: compatibility mode not supported with the requested with options\n");
+ bail = true;
+ }
+#if !(HAVE_DECL_IP_TOS)
+ if (isOverrideTOS(mExtSettings) || mExtSettings->mTOS) {
+ unsetOverrideTOS(mExtSettings);
+ fprintf(stderr, "WARN: IP_TOS not supported\n");
+ }
+#endif
+ if (isPermitKey(mExtSettings)) {
+ if (isUDP(mExtSettings)) {
+ fprintf(stderr, "ERROR: Option of --permit-key not supported with UDP\n");
+ bail = true;
+ } else if (mExtSettings->mPermitKey[0] != '\0') {
+ int keylen = strnlen(mExtSettings->mPermitKey, MAX_PERMITKEY_LEN + 1);
+ if (keylen < MIN_PERMITKEY_LEN) {
+ fprintf(stderr, "ERROR: value for --permit-key must have at least %d characters\n", MIN_PERMITKEY_LEN);
+ bail = true;
+ } else if (keylen > MAX_PERMITKEY_LEN) {
+ fprintf(stderr, "ERROR: value for --permit-key can't be more than %d characters \n", MAX_PERMITKEY_LEN);
+ bail = true;
+ }
+ }
+ }
+#if !HAVE_IPV6
+ if (isIPV6(mExtSettings)) {
+ fprintf(stderr, "ERROR: ipv6 not supported\n");
+ bail = true;
+ }
+#endif
+ if (isIPV4(mExtSettings) && isIPV6(mExtSettings)) {
+ fprintf(stderr, "WARN: both ipv4 and ipv6 set\n");
+ unsetIPV6(mExtSettings);
+ unsetIPV4(mExtSettings);
+ }
+ if (mExtSettings->mThreadMode == kMode_Client) {
+ if (isRemoveService(mExtSettings)) {
+ // -R on the client is overloaded and is the
+ // short option for --reverse, fix it now
+ setReverse(mExtSettings);
+ unsetRemoveService(mExtSettings);
+ }
+ if (isIncrSrcIP(mExtSettings) && (mExtSettings->mLocalhost==NULL)) {
+ fprintf(stderr, "ERROR: option of --incr-srcip requires -B bind option to be set\n");
+ bail = true;
+ }
+ if (isSumServerDstIP(mExtSettings)) {
+ fprintf(stderr, "WARN: option of --sum-dstip not supported on the client\n");
+ }
+ if (isOverrideTOS(mExtSettings)) {
+ unsetOverrideTOS(mExtSettings);
+ fprintf(stderr, "WARN: option of --tos-override not supported on the client\n");
+ }
+ if (isRxClamp(mExtSettings)) {
+ fprintf(stderr, "WARN: option of --tcp-rx-window-clamp not supported on the client\n");
+ unsetRxClamp(mExtSettings);
+ }
+ if (isJitterHistogram(mExtSettings)) {
+ fprintf(stderr, "WARN: option of --jitter-histogram not supported on the client\n");
+ unsetJitterHistogram(mExtSettings);
+ }
+ if (isIncrSrcPort(mExtSettings) && !mExtSettings->mBindPort) {
+ fprintf(stderr, "WARN: option of --incr-srcport requires -B bind option w/port to be set\n");
+ unsetIncrSrcPort(mExtSettings);
+ }
+ if (isPeriodicBurst(mExtSettings)) {
+ setEnhanced(mExtSettings);
+ setFrameInterval(mExtSettings);
+ }
+ if (mExtSettings->mTTL > MAXTTL) {
+ fprintf(stderr, "ERROR: option of -ttl (-T) must be less than %d\n", MAXTTL);
+ bail = true;
+ }
+ if (isPermitKey(mExtSettings) && (mExtSettings->mPermitKey[0] == '\0')) {
+ fprintf(stderr, "ERROR: option of --permit-key requires a value on the client\n");
+ bail = true;
+ }
+ if (!isUDP(mExtSettings) && isTxHoldback(mExtSettings) && isTxStartTime(mExtSettings)) {
+ fprintf(stderr,"ERROR: options of --txstart-time and --txdelay-time are mutually exclusive\n");
+ bail = true;
+ } else if (isTxStartTime(mExtSettings) || isTxHoldback(mExtSettings)) {
+ Timestamp now;
+ if (isTxStartTime(mExtSettings)) {
+ if (mExtSettings->txstart_epoch.tv_sec < 0) {
+ fprintf(stderr, "ERROR: --txstart-time must be a positive value\n");
+ unsetTxStartTime(mExtSettings);
+ bail = true;
+ } else if ((mExtSettings->txstart_epoch.tv_sec - now.getSecs()) > MAXDIFFTXSTART) {
+ char timestr[120];
+ struct timeval t;
+ t.tv_sec = now.getSecs();
+ t.tv_usec = now.getUsecs();
+ iperf_formattime(timestr, sizeof(timestr), t, isEnhanced(mExtSettings), isUTC(mExtSettings), YearThruSecTZ);
+ printf(error_starttime_exceeds, mExtSettings->txstart_epoch.tv_sec, mExtSettings->txstart_epoch.tv_usec, \
+ timestr, MAXDIFFTXSTART);
+ bail = true;
+ }
+ } else if (mExtSettings->txholdback_timer.tv_sec > MAXDIFFTXDELAY) {
+ printf(error_delaytime_exceeds, mExtSettings->txholdback_timer.tv_sec, MAXDIFFTXDELAY);
+ bail = true;
+ }
+ }
+ if (isBounceBack(mExtSettings)) {
+ if (static_cast<int> (mExtSettings->mBurstSize) > 0) {
+ fprintf(stderr, "WARN: options of --burst-size for bounce-back ignored, use -l sets size\n");
+ }
+ mExtSettings->mBounceBackBytes = mExtSettings->mBufLen;
+ mExtSettings->mBurstSize = mExtSettings->mBufLen;
+#if HAVE_DECL_TCP_QUICKACK
+ if (notcpbbquickack_cliset && isTcpQuickAck(mExtSettings)) {
+ fprintf(stderr, "ERROR: --tcp-quickack and --bounceback-no-quickack are mutually exclusive\n");
+ bail = true;
+ }
+ // be wary of double negatives here
+ if (!notcpbbquickack_cliset) {
+ setTcpQuickAck(mExtSettings);
+ }
+#endif
+ // default bounceback period to 1 second
+ if (!isPeriodicBurst(mExtSettings)) {
+ mExtSettings->mFPS = 1.0;
+ setPeriodicBurst(mExtSettings);
+ if (mExtSettings->mIntervalMode == kInterval_None) {
+ mExtSettings->mIntervalMode = kInterval_Time;
+ mExtSettings->mInterval = 1000000; // units is us
+ }
+ } else if (mExtSettings->mFPS <= 1e-5) {
+ unsetPeriodicBurst(mExtSettings);
+ }
+ if (isPeriodicBurst(mExtSettings) && (mExtSettings->mBounceBackBurst == -1)) {
+ mExtSettings->mBounceBackBurst = 10;
+ }
+ }
+#if HAVE_DECL_TCP_NOTSENT_LOWAT
+ if (isTripTime(mExtSettings) && !isUDP(mExtSettings)) {
+ if (isWritePrefetch(mExtSettings)) {
+ if (mExtSettings->mWritePrefetch <= 0) {
+ unsetWritePrefetch(mExtSettings);
+ }
+ } else {
+ mExtSettings->mWritePrefetch = SMALL_WRITE_PREFETCH;
+ setWritePrefetch(mExtSettings);
+ setEnhanced(mExtSettings);
+ }
+ }
+#endif
+ if (isPeriodicBurst(mExtSettings)) {
+ if (isIsochronous(mExtSettings)) {
+ fprintf(stderr, "ERROR: options of --burst-period and --isochronous cannot be applied together\n");
+ bail = true;
+ } else if (isNearCongest(mExtSettings)) {
+ fprintf(stderr, "ERROR: options of --burst-period and --near-congestion cannot be applied together\n");
+ bail = true;
+ } else if (static_cast<int> (mExtSettings->mBurstSize) == 0) {
+ mExtSettings->mBurstSize = byte_atoi("1M"); //default to 1 Mbyte
+ }
+ if (static_cast<int> (mExtSettings->mBurstSize) < mExtSettings->mBufLen) {
+ fprintf(stderr, "ERROR: option of --burst-size %d must be equal or larger to write length (-l) %d\n", mExtSettings->mBurstSize, mExtSettings->mBufLen);
+ bail = true;
+ }
+ } else if (!isBounceBack(mExtSettings) && (static_cast<int> (mExtSettings->mBurstSize) > 0)) {
+ setPeriodicBurst(mExtSettings);
+ mExtSettings->mFPS = 1.0;
+ fprintf(stderr, "WARN: option of --burst-size without --burst-period defaults --burst-period to 1 second\n");
+ }
+ if (isUDP(mExtSettings)) {
+ if (isPeerVerDetect(mExtSettings)) {
+ fprintf(stderr, "ERROR: option of -X or --peer-detect not supported with -u UDP\n");
+ bail = true;
+ }
+ if (isConnectOnly(mExtSettings)) {
+ fprintf(stderr, "ERROR: option of --connect-only not supported with -u UDP\n");
+ bail = true;
+ }
+ if (isBounceBack(mExtSettings)) {
+ fprintf(stderr, "ERROR: option of -X or --bounceback not supported with -u UDP\n");
+ bail = true;
+ }
+ if (isNearCongest(mExtSettings)) {
+ fprintf(stderr, "ERROR: option of --near-congestion not supported with -u UDP\n");
+ bail = true;
+ }
+ if (isIPG(mExtSettings) && isBWSet(mExtSettings)) {
+ fprintf(stderr, "ERROR: options of --b and --ipg cannot be applied together\n");
+ bail = true;
+ }
+ if (mExtSettings->mBurstIPG < 0.0) {
+ fprintf(stderr, "ERROR: option --ipg must be a positive value\n");
+ bail = true;
+ }
+ if (mExtSettings->mConnectRetries > 0) {
+ fprintf(stderr, "ERROR: option --connect-retries not supported with -u UDP\n");
+ bail = true;
+ }
+ if (isWritePrefetch(mExtSettings)) {
+ fprintf(stderr, "WARN: setting of option --tcp-write-prefetch is not supported with -u UDP\n");
+ unsetWritePrefetch(mExtSettings);
+ }
+ if (isTcpQuickAck(mExtSettings)) {
+ fprintf(stderr, "WARN: setting of option --tcp-quickack is not supported with -u UDP\n");
+ unsetWritePrefetch(mExtSettings);
+ }
+
+ {
+ double delay_target;
+ if (isIPG(mExtSettings)) {
+ delay_target = mExtSettings->mBurstIPG * 1e9; // convert from seconds to nanoseconds
+ } else {
+ // compute delay target in units of nanoseconds
+ if (mExtSettings->mAppRateUnits == kRate_BW) {
+ // compute delay for bandwidth restriction, constrained to [0,max] seconds
+ delay_target = (mExtSettings->mBufLen * 8e9) / mExtSettings->mAppRate;
+ } else {
+ delay_target = 1e9 / mExtSettings->mAppRate;
+ }
+ }
+ if (delay_target < 0 ||
+ delay_target > MAXIPGSECS * 1e9) {
+ fprintf(stderr, "ERROR: IPG delay target of %.1f secs too large (max value is %d secs)\n", (delay_target / 1e9), MAXIPGSECS);
+ bail = true;
+ }
+ }
+ if (isTripTime(mExtSettings)) {
+ if (mExtSettings->mBufLen < MINTRIPTIMEPLAYOAD) {
+ if (isReverse(mExtSettings) || isFullDuplex(mExtSettings) || (mExtSettings->mMode != kTest_Normal)) {
+ fprintf(stderr, "ERROR: payload (-l) size of %d too small for --trip-times, must be %d or greater\n", mExtSettings->mBufLen, MINTRIPTIMEPLAYOAD);
+ bail = true;
+ } else {
+ setSmallTripTime(mExtSettings);
+ }
+ }
+ }
+ } else {
+ if ((mExtSettings->mAppRate > 0) && isNearCongest(mExtSettings)) {
+ fprintf(stderr, "ERROR: option of --near-congestion and -b rate limited are mutually exclusive\n");
+ bail = true;
+ }
+ if (isBWSet(mExtSettings) && !(mExtSettings->mAppRateUnits == kRate_PPS) \
+ && ((mExtSettings->mAppRate / 8) < static_cast<uintmax_t>(mExtSettings->mBufLen))) {
+ fprintf(stderr, "ERROR: option -b and -l of %d are incompatible, consider setting -l to %d or lower\n", \
+ mExtSettings->mBufLen, static_cast<int>(mExtSettings->mAppRate / 8));
+ bail = true;
+ }
+ if (isIPG(mExtSettings)) {
+ fprintf(stderr, "ERROR: option --ipg requires -u UDP\n");
+ bail = true;
+ }
+ if (isNoUDPfin(mExtSettings)) {
+ fprintf(stderr, "ERROR: option --no-udp-fin requires -u UDP\n");
+ bail = true;
+ }
+ if (isTxHoldback(mExtSettings) && isConnectOnly(mExtSettings)) {
+ fprintf(stderr,"ERROR: Fail because --txdelay-time and --connect-only cannot be applied together\n");
+ bail = true; ;
+ }
+ if (mExtSettings->mAppRateUnits == kRate_PPS) {
+ fprintf(stderr, "ERROR: units of pps not supported with TCP\n");
+ bail = true;
+ }
+ if (isJitterHistogram(mExtSettings)) {
+ fprintf(stderr, "ERROR: option of --jitter-histogram not supported with TCP\n");
+ bail = true;
+ }
+ }
+ if (!isReverse(mExtSettings) && !isFullDuplex(mExtSettings) && isHistogram(mExtSettings)){
+ setTcpWriteTimes(mExtSettings);
+ mExtSettings->mHistBins = 100000; // 10 seconds wide
+ mExtSettings->mHistBinsize = 100; // 100 usec bins
+ mExtSettings->mHistUnits = 6; // usecs 10 pow(x)
+ mExtSettings->mHistci_lower = 5;
+ mExtSettings->mHistci_upper = 95;
+ }
+ if (isCongestionControl(mExtSettings) && isReverse(mExtSettings)) {
+ fprintf(stderr, "ERROR: tcp congestion control -Z and --reverse cannot be applied together\n");
+ bail = true;
+ }
+ {
+ int one_only = 0;
+ if (isFullDuplex(mExtSettings))
+ one_only++;
+ if (isReverse(mExtSettings))
+ one_only++;
+ if (mExtSettings->mMode != kTest_Normal)
+ one_only++;
+ if (one_only > 1) {
+ fprintf(stderr, "ERROR: options of --full-duplex, --reverse, -d and -r are mutually exclusive\n");
+ bail = true;
+ }
+ }
+ if (isBWSet(mExtSettings) && isIsochronous(mExtSettings)) {
+ fprintf(stderr, "ERROR: options of --b and --isochronous cannot be applied together\n");
+ bail = true;
+ }
+ } else {
+ if (mExtSettings->mPortLast && (!(mExtSettings->mPortLast >= mExtSettings->mPort))) {
+ fprintf(stderr, "ERROR: invalid port range of %d-%d\n",mExtSettings->mPort, mExtSettings->mPortLast);
+ bail = true;
+ }
+ if (isPermitKey(mExtSettings)) {
+ if (mExtSettings->mPermitKey[0]=='\0') {
+ generate_permit_key(mExtSettings);
+ }
+ if (mExtSettings->mListenerTimeout < 0) {
+ mExtSettings->mListenerTimeout = DEFAULT_PERMITKEY_LIFE;
+ }
+ }
+ if (isBounceBack(mExtSettings)) {
+ fprintf(stderr, "ERROR: setting of option --bounce-back is not supported on the server\n");
+ bail = true;
+ }
+ if (isTripTime(mExtSettings)) {
+ fprintf(stderr, "WARN: setting of option --trip-times is not supported on the server\n");
+ }
+ if (isWritePrefetch(mExtSettings)) {
+ fprintf(stderr, "WARN: setting of option --tcp-write-prefetch is not supported on the server\n");
+ unsetWritePrefetch(mExtSettings);
+ }
+ if (isIncrSrcIP(mExtSettings)) {
+ fprintf(stderr, "WARN: setting of option --incr-srcip is not supported on the server\n");
+ }
+ if (isVaryLoad(mExtSettings)) {
+ fprintf(stderr, "WARN: option of variance per -b is not supported on the server\n");
+ }
+ if (isTxStartTime(mExtSettings)) {
+ fprintf(stderr, "WARN: option of --txstart-time is not supported on the server\n");
+ }
+ if (isTxHoldback(mExtSettings)) {
+ fprintf(stderr, "WARN: option of --txdelay-time is not supported on the server\n");
+ }
+ if (isIPG(mExtSettings)) {
+ fprintf(stderr, "WARN: option of --ipg is not supported on the server\n");
+ }
+ if (isIsochronous(mExtSettings)) {
+ fprintf(stderr, "WARN: option of --isochronous is not supported on the server\n");
+ }
+ if (isFullDuplex(mExtSettings)) {
+ fprintf(stderr, "WARN: option of --full-duplex is not supported on the server\n");
+ }
+ if (isReverse(mExtSettings)) {
+ fprintf(stderr, "WARN: option of --reverse is not supported on the server\n");
+ }
+ if (isIncrDstIP(mExtSettings)) {
+ fprintf(stderr, "WARN: option of --incr-dstip is not supported on the server\n");
+ }
+ if (isFQPacing(mExtSettings)) {
+ fprintf(stderr, "WARN: option of --fq-rate is not supported on the server\n");
+ }
+ if (isNoUDPfin(mExtSettings)) {
+ fprintf(stderr, "WARN: option of --no-udp-fin is not supported on the server\n");
+ }
+ if (isPeerVerDetect(mExtSettings)) {
+ fprintf(stderr, "WARN: option of -X or --peer-detect not supported on the server\n");
+ }
+ if (mExtSettings->mConnectRetries > 0) {
+ fprintf(stderr, "WARN: option --connect-retries not supported on the server\n");
+ }
+ if (isNearCongest(mExtSettings)) {
+ fprintf(stderr, "WARN: option of --near-congestion not supported on the server\n");
+ }
+ if (isPeriodicBurst(mExtSettings)) {
+ fprintf(stderr, "WARN: option of --burst-period can only be set on the client\n");
+ }
+ if (mExtSettings->mBurstSize != 0) {
+ fprintf(stderr, "WARN: option of --burst-size not supported on the server\n");
+ }
+ if (isUDP(mExtSettings) && isRxClamp(mExtSettings)) {
+ fprintf(stderr, "WARN: option of --tcp-rx-window-clamp not supported using -u UDP \n");
+ unsetRxClamp(mExtSettings);
+ }
+ }
+ if (bail)
+ exit(1);
+
+ // Histogram optional settings
+ if (isHistogram(mExtSettings)) {
+ if (!mExtSettings->mHistogramStr) {
+ if (mExtSettings->mThreadMode == kMode_Server) {
+ // set default rx histogram settings, milliseconds bins between 0 and 1 secs
+ mExtSettings->mHistBins = 1000;
+ mExtSettings->mHistBinsize = 1;
+ mExtSettings->mHistUnits = 3;
+ mExtSettings->mHistci_lower = 5;
+ mExtSettings->mHistci_upper = 95;
+ } else {
+ // set default tx histogram settings, microseconds with 100 us bins
+ mExtSettings->mHistBins = 10000;
+ mExtSettings->mHistBinsize = 100;
+ mExtSettings->mHistUnits = 6;
+ mExtSettings->mHistci_lower = 5;
+ mExtSettings->mHistci_upper = 95;
+ }
+ } else {
+ // check for optional arguments to change histogram settings
+ if (((results = strtok(mExtSettings->mHistogramStr, ",")) != NULL) && !strcmp(results,mExtSettings->mHistogramStr)) {
+ // scan for unit specifier
+ char *tmp = new char [strlen(results) + 1];
+ strcpy(tmp, results);
+ if ((strtok(tmp, "u") != NULL) && strcmp(results,tmp) != 0) {
+ mExtSettings->mHistUnits = 6; // units is microseconds
+ } else {
+ strcpy(tmp, results);
+ if ((strtok(tmp, "m") != NULL) && strcmp(results,tmp) != 0) {
+ mExtSettings->mHistUnits = 3; // units is milliseconds
+ }
+ }
+ mExtSettings->mHistBinsize = atoi(tmp);
+ delete [] tmp;
+ mExtSettings->mHistci_lower = 5;
+ mExtSettings->mHistci_upper = 95;
+ if ((results = strtok(results+strlen(results)+1, ",")) != NULL) {
+ mExtSettings->mHistBins = byte_atoi(results);
+ if ((results = strtok(NULL, ",")) != NULL) {
+ mExtSettings->mHistci_lower = atof(results);
+ if ((results = strtok(NULL, ",")) != NULL) {
+ mExtSettings->mHistci_upper = atof(results);
+ }
+ }
+ }
+ }
+ }
+ }
+ if (isUDP(mExtSettings)) {
+ if (isFullDuplex(mExtSettings)) {
+ setNoUDPfin(mExtSettings);
+ }
+ if (mExtSettings->mThreadMode == kMode_Client) {
+ // L2 settings
+ if (l2checks && isUDP(mExtSettings)) {
+ l2checks = 0;
+#if defined(HAVE_LINUX_FILTER_H) && defined(HAVE_AF_PACKET)
+ // Request server to do length checks
+ setL2LengthCheck(mExtSettings);
+#else
+ fprintf(stderr, "WARNING: option --l2checks not supported on this platform\n");
+#endif
+ }
+ }
+ } else {
+ if (mExtSettings->mBurstSize && (static_cast<int>(mExtSettings->mBurstSize) < mExtSettings->mBufLen)) {
+ fprintf(stderr, "WARN: Setting --burst-size to %d because value given is smaller than -l value\n", \
+ mExtSettings->mBufLen);
+ mExtSettings->mBurstSize = mExtSettings->mBufLen;
+ } else if (isTripTime(mExtSettings) && !(mExtSettings->mBurstSize > 0)) {
+ mExtSettings->mBurstSize = mExtSettings->mBufLen;
+ }
+ }
+ if (isIsochronous(mExtSettings) && mExtSettings->mIsochronousStr) {
+ // parse client isochronous field,
+ // format is --isochronous <int>:<float>,<float> and supports
+ // human suffixes, e.g. --isochronous 60:100m,5m
+ // which is frames per second, mean and variance
+ if (mExtSettings->mThreadMode == kMode_Client) {
+ if (((results = strtok(mExtSettings->mIsochronousStr, ":")) != NULL) && !strcmp(results,mExtSettings->mIsochronousStr)) {
+ mExtSettings->mFPS = atof(results);
+ if ((results = strtok(NULL, ",")) != NULL) {
+ mExtSettings->mMean = bitorbyte_atof(results);
+ if (mExtSettings->mMean == 0.0) {
+ fprintf(stderr, "ERROR: Invalid --isochronous mean value, must be greater than zero\n");
+ exit(1);
+ }
+ if (!isUDP(mExtSettings)) {
+ if (mExtSettings->mMean < 0) {
+ fprintf(stderr, "ERROR: Invalid --isochronous units of 'p' with TCP must be bytes\n");
+ exit(1);
+ }
+ } else { // UDP
+ if (mExtSettings->mMean < 0) {
+ mExtSettings->mMean *= -8 * mExtSettings->mBufLen * mExtSettings->mFPS;
+ }
+ }
+ if ((results = strtok(NULL, ",")) != NULL) {
+ mExtSettings->mVariance = bitorbyte_atof(results);
+ }
+ } else {
+ mExtSettings->mMean = 20000000.0;
+ mExtSettings->mVariance = 0.0;
+ }
+ } else {
+ fprintf(stderr, "WARNING: Invalid --isochronous value, format is <fps>:<mean>,<variance> (e.g. 60:18M,1m)\n");
+ }
+ }
+ }
+
+ // Check for further mLocalhost (-B) and <dev> requests
+ // full addresses look like 192.168.1.1:6001%eth0 or [2001:e30:1401:2:d46e:b891:3082:b939]:6001%eth0
+ iperf_sockaddr tmp;
+ // Parse -B addresses
+ if (mExtSettings->mLocalhost) {
+ if (((results = strtok(mExtSettings->mLocalhost, "%")) != NULL) && ((results = strtok(NULL, "%")) != NULL)) {
+ mExtSettings->mIfrname = static_cast<char *>(calloc(strlen(results) + 1, sizeof(char)));
+ strcpy(mExtSettings->mIfrname, results);
+ if (mExtSettings->mThreadMode == kMode_Client) {
+ fprintf(stderr, "WARNING: Client cannot set bind device %s via -B consider using -c\n", mExtSettings->mIfrname);
+ free(mExtSettings->mIfrname);
+ mExtSettings->mIfrname = NULL;
+ }
+ }
+ if (isIPV6(mExtSettings)) {
+ results = isv6_bracketed_port(mExtSettings->mLocalhost);
+ } else {
+ results = isv4_port(mExtSettings->mLocalhost);
+ }
+ if (results) {
+ if (mExtSettings->mThreadMode == kMode_Client) {
+ mExtSettings->mBindPort = atoi(results);
+ } else {
+ fprintf(stderr, "WARNING: port %s ignored - set receive port on server via -p or -L\n", results);
+ }
+ }
+ // Check for multicast per the -B
+ SockAddr_setHostname(mExtSettings->mLocalhost, &tmp,
+ (isIPV6(mExtSettings) ? 1 : 0));
+ if ((mExtSettings->mThreadMode != kMode_Client) && SockAddr_isMulticast(&tmp)) {
+ setMulticast(mExtSettings);
+ } else if (SockAddr_isMulticast(&tmp)) {
+ if (mExtSettings->mIfrname) {
+ free(mExtSettings->mIfrname);
+ mExtSettings->mIfrname = NULL;
+ }
+ fprintf(stderr, "WARNING: Client src addr (per -B) must be ip unicast\n");
+ }
+ }
+ // Parse client (-c) addresses for multicast, link-local and bind to device, port incr
+ if (mExtSettings->mThreadMode == kMode_Client) {
+ if (mExtSettings->mPortLast > mExtSettings->mPort) {
+ int prcnt = ((mExtSettings->mPortLast - mExtSettings->mPort) + 1);
+ int threads_needed = (prcnt > mExtSettings->mThreads) ? prcnt : mExtSettings->mThreads;
+ if (mExtSettings->mThreads < prcnt) {
+ fprintf(stderr, "WARNING: port-range and -P mismatch (adjusting -P to %d)\n", threads_needed);
+ mExtSettings->mThreads = threads_needed;
+ } else if ((mExtSettings->mThreads > 1) && (threads_needed > prcnt)) {
+ fprintf(stderr, "WARNING: port-range and -P mismatch (adjusting port-range to %d-%d)\n", \
+ mExtSettings->mPort, mExtSettings->mPort + threads_needed);
+ }
+ }
+ mExtSettings->mIfrnametx = NULL; // default off SO_BINDTODEVICE
+ if (((results = strtok(mExtSettings->mHost, "%")) != NULL) && ((results = strtok(NULL, "%")) != NULL)) {
+ size_t len = strlen(results) + 1;
+ mExtSettings->mIfrnametx = static_cast<char *>(calloc(len, sizeof(char)));
+ strncpy(mExtSettings->mIfrnametx, results, len);
+ mExtSettings->mIfrnametx[len] = '\0';
+ }
+ if (isIPV6(mExtSettings))
+ strip_v6_brackets(mExtSettings->mHost);
+ // get the socket address settings from the host, needed for link-local and multicast tests
+ SockAddr_zeroAddress(&mExtSettings->peer);
+ SockAddr_remoteAddr(mExtSettings);
+ if (isIPV6(mExtSettings) && SockAddr_isLinklocal(&mExtSettings->peer)) {
+ // link-local doesn't use SO_BINDTODEVICE but includes it in the host string
+ // so stitch things back together and null the bind to name
+ if (mExtSettings->mIfrnametx) {
+ strcat(mExtSettings->mHost, "%");
+ strcat(mExtSettings->mHost, mExtSettings->mIfrnametx);
+ free(mExtSettings->mIfrnametx);
+ mExtSettings->mIfrnametx = NULL;
+ } else {
+ fprintf(stderr, "WARNING: usage of ipv6 link-local requires a device specifier, e.g. %s%%eth0\n", mExtSettings->mHost);
+ }
+ SockAddr_zeroAddress(&mExtSettings->peer); // for link-local, force getaddrinfo() in the connect
+ }
+ if (SockAddr_isMulticast(&mExtSettings->peer)) {
+ bail = false;
+ if ((mExtSettings->mThreads > 1) && !isIncrDstIP(mExtSettings)) {
+ fprintf(stderr, "ERROR: client option of -P greater than 1 not supported with multicast address\n");
+ bail = true;
+ } else if (isFullDuplex(mExtSettings) || isReverse(mExtSettings) || (mExtSettings->mMode != kTest_Normal)) {
+ fprintf(stderr, "ERROR: options of --full-duplex, --reverse, -d and -r not supported with multicast addresses\n");
+ bail = true;
+ }
+ if (bail)
+ exit(1);
+ else
+ setMulticast(mExtSettings);
+ }
+#ifndef HAVE_DECL_SO_BINDTODEVICE
+ if (mExtSettings->mIfrnametx) {
+ fprintf(stderr, "bind to device will be ignored because not supported\n");
+ free(mExtSettings->mIfrnametx);
+ mExtSettings->mIfrnametx=NULL;
+ }
+#endif
+ }
+ if ((mExtSettings->mIntervalMode == kInterval_Time) && (mExtSettings->mIntervalMode <= 0)) {
+ mExtSettings->mIntervalMode = kInterval_None;
+ mExtSettings->mInterval = 0;
+ }
+}
+
+void Settings_GetUpperCaseArg (const char *inarg, char *outarg) {
+ int len = strlen(inarg);
+ strcpy(outarg,inarg);
+
+ if ((len > 0) && (inarg[len-1] >='a')
+ && (inarg[len-1] <= 'z'))
+ outarg[len-1]= outarg[len-1]+'A'-'a';
+}
+
+void Settings_GetLowerCaseArg (const char *inarg, char *outarg) {
+ int len = strlen(inarg);
+ strcpy(outarg,inarg);
+
+ if ((len > 0) && (inarg[len-1] >='A')
+ && (inarg[len-1] <= 'Z'))
+ outarg[len-1]= outarg[len-1]-'A'+'a';
+}
+
+/*
+ * Settings_GenerateListenerSettings
+ * Called to generate the settings to be passed to the Listener
+ * instance that will handle dual testings from the client side
+ * this should only return an instance if it was called on
+ * the struct thread_settings instance generated from the command line
+ * for client side execution
+ */
+#define DUALTIMER_MS 300
+void Settings_GenerateListenerSettings (struct thread_Settings *client, struct thread_Settings **listener) {
+ if ((client->mMode == kTest_DualTest) || (client->mMode == kTest_TradeOff)) {
+ Settings_Copy(client, listener, SHALLOW_COPY);
+ unsetDaemon((*listener));
+ setCompat((*listener));
+ if (client->mListenPort != 0) {
+ (*listener)->mPort = client->mListenPort;
+ } else {
+ (*listener)->mPort = client->mPort;
+ }
+ if (client->mMode == kTest_TradeOff) {
+ (*listener)->mAmount = client->mAmount + DUALTIMER_MS;
+ } else if (client->mMode == kTest_DualTest) {
+ (*listener)->mAmount = client->mAmount + (SLOPSECS * 100);
+ }
+ if ((client->mMode != kTest_Normal) && ((*listener)->mAmount < DUALTIMER_MS)) {
+ (*listener)->mAmount = DUALTIMER_MS;
+ }
+ (*listener)->mFileName = NULL;
+ (*listener)->mHost = NULL;
+ (*listener)->mLocalhost = NULL;
+ (*listener)->mOutputFileName = NULL;
+ (*listener)->mMode = kTest_Normal;
+ (*listener)->mThreadMode = kMode_Listener;
+ if (client->mHost != NULL) {
+ (*listener)->mHost = new char[strlen(client->mHost) + 1];
+ strcpy((*listener)->mHost, client->mHost);
+ }
+ if (client->mLocalhost != NULL) {
+ (*listener)->mLocalhost = new char[strlen(client->mLocalhost) + 1];
+ strcpy((*listener)->mLocalhost, client->mLocalhost);
+ }
+ if (client->mBufLen <= 0) {
+ if (isUDP((*listener))) {
+ (*listener)->mBufLen = kDefault_UDPBufLen;
+ } else {
+ (*listener)->mBufLen = kDefault_TCPBufLen;
+ }
+ } else {
+ (*listener)->mBufLen = client->mBufLen;
+ }
+ setReport((*listener));
+ } else {
+ *listener = NULL;
+ }
+}
+
+void Settings_ReadClientSettingsIsoch (struct thread_Settings **client, struct client_hdrext_isoch_settings *hdr) {
+ (*client)->mFPS = ntohl(hdr->FPSl);
+ (*client)->mFPS += ntohl(hdr->FPSu) / static_cast<double>(rMillion);
+ (*client)->mMean = ntohl(hdr->Meanl);
+ (*client)->mMean += ntohl(hdr->Meanu) / static_cast<double>(rMillion);
+ (*client)->mVariance = ntohl(hdr->Variancel);
+ (*client)->mVariance += ntohl(hdr->Varianceu) / static_cast<double>(rMillion);
+ (*client)->mBurstIPG = ntohl(hdr->BurstIPGl);
+ (*client)->mBurstIPG += ntohl(hdr->BurstIPGu) / static_cast<double>(rMillion);
+}
+
+void Settings_ReadClientSettingsBurst (struct thread_Settings **client, struct client_hdrext_isoch_settings *hdr) {
+ (*client)->mFPS = ntohl(hdr->FPSl);
+ (*client)->mFPS += ntohl(hdr->FPSu) / static_cast<double>(rMillion);
+}
+
+void Settings_ReadClientSettingsV1 (struct thread_Settings **client, struct client_hdr_v1 *hdr) {
+ (*client)->mTID = thread_zeroid();
+ (*client)->mPort = static_cast<unsigned short>(ntohl(hdr->mPort));
+ (*client)->mThreads = 1;
+ struct thread_Settings *tmpSettings = *client;
+ if (hdr->mBufLen != 0) {
+ (*client)->mBufLen = ntohl(hdr->mBufLen);
+ setBuflenSet(tmpSettings);
+ } else {
+#ifdef DEFAULT_PAYLOAD_LEN_PER_MTU_DISCOVERY
+ checksock_max_udp_payload(tmpSettings);
+#endif
+ }
+ (*client)->mAmount = ntohl(hdr->mAmount);
+ if (((*client)->mAmount & 0x80000000) > 0) {
+ setModeTime((*client));
+#ifndef WIN32
+ (*client)->mAmount |= 0xFFFFFFFF00000000LL;
+#else
+ (*client)->mAmount |= 0xFFFFFFFF00000000;
+#endif
+ (*client)->mAmount = -(*client)->mAmount;
+ } else {
+ unsetModeTime((*client));
+ }
+}
+
+/*
+ * Settings_GenerateClientSettings
+ *
+ * Called by the Listener to generate the settings to be used by clients
+ * per things like dual tests. Set client pointer to null if a client isn't needed
+ *
+ * Note: mBuf should already be filled out per the Listener's apply_client_settings
+ */
+void Settings_GenerateClientSettings (struct thread_Settings *server, struct thread_Settings **client, void *mBuf) {
+ assert(server != NULL);
+ assert(mBuf != NULL);
+ uint32_t flags = isUDP(server) ? ntohl(*(uint32_t *)((char *)mBuf + sizeof(struct UDP_datagram))) : ntohl(*(uint32_t *)mBuf);
+ uint16_t upperflags = 0;
+ thread_Settings *reversed_thread = NULL;
+ *client = NULL;
+ bool v1test = (flags & HEADER_VERSION1) && !(flags & HEADER_VERSION2);
+#ifdef HAVE_THREAD_DEBUG
+ if (v1test)
+ thread_debug("header set for a version 1 test");
+#endif
+ if (isFullDuplex(server) || isServerReverse(server))
+ setTransferID(server, 1);
+ if (isFullDuplex(server) || v1test) {
+ Settings_Copy(server, client, SHALLOW_COPY);
+ reversed_thread = *client;
+ if (isFullDuplex(server) && !(flags & HEADER_VERSION1)) {
+ setFullDuplex(reversed_thread);
+ } else {
+ unsetFullDuplex(reversed_thread);
+ }
+ } else if (isServerReverse(server)) {
+ reversed_thread = server;
+ } else {
+ assert(0);
+ return;
+ }
+ reversed_thread->mThreadMode = kMode_Client;
+
+ if (isUDP(server)) { // UDP test information passed in every packet per being stateless
+ struct client_udp_testhdr *hdr = static_cast<struct client_udp_testhdr *>(mBuf);
+ Settings_ReadClientSettingsV1(&reversed_thread, &hdr->base);
+ if (isFullDuplex(server) || v1test) {
+ server->mAmount = reversed_thread->mAmount + (SLOPSECS * 100);
+ }
+ if (v1test) {
+ setServerReverse(reversed_thread);
+ if (flags & RUN_NOW) {
+ reversed_thread->mMode = kTest_DualTest;
+ } else {
+ reversed_thread->mMode = kTest_TradeOff;
+ }
+ }
+ if (flags & HEADER_EXTEND) {
+ reversed_thread->mAppRate = ntohl(hdr->extend.lRate);
+#ifdef HAVE_INT64_T
+ reversed_thread->mAppRate |= (static_cast<uint64_t>(ntohl(hdr->extend.uRate) >> 8) << 32);
+#endif
+ upperflags = ntohs(hdr->extend.upperflags);
+ if (upperflags & HEADER_NOUDPFIN) {
+ setNoUDPfin(reversed_thread);
+ }
+ if ((upperflags & HEADER_UNITS_PPS) == HEADER_UNITS_PPS) {
+ reversed_thread->mAppRateUnits = kRate_PPS;
+ } else {
+ reversed_thread->mAppRateUnits = kRate_BW;
+ }
+ reversed_thread->mTOS = ntohs(hdr->extend.tos);
+ if (isIsochronous(server)) {
+ Settings_ReadClientSettingsIsoch(&reversed_thread, &hdr->isoch_settings);
+ }
+ if (upperflags & HEADER_FQRATESET) {
+ setFQPacing(reversed_thread);
+ reversed_thread->mFQPacingRate = ntohl(hdr->start_fq.fqratel);
+#ifdef HAVE_INT64_T
+ reversed_thread->mFQPacingRate |= (static_cast<uint64_t>(ntohl(hdr->start_fq.fqrateu)) << 32);
+#endif
+ }
+ }
+ } else { //tcp first payload
+ struct client_tcp_testhdr *hdr = static_cast<struct client_tcp_testhdr *>(mBuf);
+ Settings_ReadClientSettingsV1(&reversed_thread, &hdr->base);
+
+ if (isFullDuplex(server) || v1test) {
+ server->mAmount = reversed_thread->mAmount + (SLOPSECS * 100);
+ }
+ if (v1test) {
+ setServerReverse(reversed_thread);
+ if (flags & RUN_NOW) {
+ reversed_thread->mMode = kTest_DualTest;
+ } else {
+ reversed_thread->mMode = kTest_TradeOff;
+ }
+ }
+ if (flags & HEADER_EXTEND) {
+ reversed_thread->mAppRate = ntohl(hdr->extend.lRate);
+#ifdef HAVE_INT64_T
+ reversed_thread->mAppRate |= (static_cast<uint64_t>(ntohl(hdr->extend.uRate) >> 8) << 32);
+#endif
+ upperflags = ntohs(hdr->extend.upperflags);
+ reversed_thread->mTOS = ntohs(hdr->extend.tos);
+
+ if (isIsochronous(server)) {
+ Settings_ReadClientSettingsIsoch(&reversed_thread, &hdr->isoch_settings);
+ } else if (isPeriodicBurst(server)) {
+ Settings_ReadClientSettingsBurst(&reversed_thread, &hdr->isoch_settings);
+ }
+ if (upperflags & HEADER_FQRATESET) {
+ setFQPacing(reversed_thread);
+ reversed_thread->mFQPacingRate = ntohl(hdr->start_fq.fqratel);
+#ifdef HAVE_INT64_T
+ reversed_thread->mFQPacingRate |= (static_cast<uint64_t>(ntohl(hdr->start_fq.fqrateu)) << 32);
+#endif
+ }
+ }
+ }
+ unsetTxHoldback(reversed_thread);
+ setNoSettReport(reversed_thread);
+ setNoConnectSync(reversed_thread);
+ // for legacy -d and -r need so set the reversed threads mHost
+ if (v1test) {
+ reversed_thread->mHost = new char[REPORT_ADDRLEN];
+ if ((reinterpret_cast<sockaddr*>(&server->peer))->sa_family == AF_INET) {
+ inet_ntop(AF_INET, &(reinterpret_cast<sockaddr_in*>(&server->peer))->sin_addr,
+ reversed_thread->mHost, REPORT_ADDRLEN);
+ }
+#if HAVE_IPV6
+ else {
+ inet_ntop(AF_INET6, &(reinterpret_cast<sockaddr_in6*>(&server->peer))->sin6_addr,
+ reversed_thread->mHost, REPORT_ADDRLEN);
+ }
+#endif
+ } else {
+ reversed_thread->mMode = kTest_Normal;
+#if HAVE_DECL_SO_MAX_PACING_RATE
+ if (isFQPacing(reversed_thread)) {
+ int rc = setsockopt(reversed_thread->mSock, SOL_SOCKET, SO_MAX_PACING_RATE, \
+ &reversed_thread->mFQPacingRate, sizeof(reversed_thread->mFQPacingRate));
+ WARN_errno(rc == SOCKET_ERROR, "setsockopt SO_MAX_PACING_RATE");
+ #ifdef HAVE_THREAD_DEBUG
+ #ifdef HAVE_INT64_T
+ thread_debug("Set socket %d pacing rate to %ld byte/sec", reversed_thread->mSock, reversed_thread->mFQPacingRate);
+ #else
+ thread_debug("Set socket %d pacing rate to %d byte/sec", reversed_thread->mSock, reversed_thread->mFQPacingRate);
+ #endif
+ #endif
+ }
+#endif // MAX_PACING_RATE
+ }
+}
+
+int Settings_GenerateClientHdrV1 (struct thread_Settings *client, struct client_hdr_v1 *hdr) {
+ if (isBuflenSet(client)) {
+ hdr->mBufLen = htonl(client->mBufLen);
+ } else {
+ hdr->mBufLen = 0;
+ }
+ if (client->mListenPort != 0) {
+ hdr->mPort = htonl(client->mListenPort);
+ } else {
+ hdr->mPort = htonl(client->mPort);
+ }
+ hdr->numThreads = htonl(client->mThreads);
+ if (isModeTime(client)) {
+ hdr->mAmount = htonl(-(long)client->mAmount);
+ } else {
+ hdr->mAmount = htonl((long)client->mAmount);
+ hdr->mAmount &= htonl(0x7FFFFFFF);
+ }
+ return (sizeof(struct client_hdr_v1));
+}
+
+/*
+ * Settings_GenerateClientHdr
+ *
+ * Called to generate the client header to be passed to the listener/server
+ *
+ * This will handle:
+ * o) dual testings from the listener/server side
+ * o) advanced udp test settings
+ *
+ * Returns size of header in bytes
+ */
+int Settings_GenerateClientHdr (struct thread_Settings *client, void *testhdr, struct timeval startTime) {
+ uint16_t len = 0;
+ uint16_t upperflags = 0;
+ uint16_t lowerflags = 0;
+ uint32_t flags = 0;
+
+ // Check the corner case of small packets and trip times
+ if (isUDP(client) && isSmallTripTime(client)) {
+ int buflen = (client->mBufLen < (int) sizeof(struct client_udpsmall_testhdr)) ? client->mBufLen \
+ : sizeof(struct client_udpsmall_testhdr);
+ struct client_udpsmall_testhdr *hdr = static_cast<struct client_udpsmall_testhdr *>(testhdr);
+ memset(hdr, 0, buflen);
+ hdr->flags = htons(HEADER16_SMALL_TRIPTIMES);
+#ifdef HAVE_THREAD_DEBUG
+ thread_debug("UDP small trip times flags = %X", ntohs(hdr->flags));
+#endif
+ return (MINIPERFPAYLOAD);
+ }
+ // flags common to both TCP and UDP
+ if (isReverse(client) && !isCompat(client)) {
+ upperflags |= HEADER_REVERSE;
+ if (isTxStartTime(client) && !TimeZero(startTime)) {
+ upperflags |= HEADER_EPOCH_START;
+ }
+ }
+ if (isFullDuplex(client) && !isCompat(client)) {
+ upperflags |= HEADER_FULLDUPLEX;
+ if (isTxStartTime(client) && !TimeZero(startTime)) {
+ upperflags |= HEADER_EPOCH_START;
+ }
+ }
+ if (isTxStartTime(client) && isTripTime(client) && !TimeZero(startTime)) {
+ upperflags |= HEADER_EPOCH_START;
+ }
+
+ // Now setup UDP and TCP specific passed settings from client to server
+ if (isUDP(client)) { // UDP test information passed in every packet per being stateless
+ struct client_udp_testhdr *hdr = static_cast<struct client_udp_testhdr *>(testhdr);
+ int buflen = (client->mBufLen < (int) sizeof(struct client_udp_testhdr)) ? client->mBufLen \
+ : sizeof(struct client_udp_testhdr);
+ memset(hdr, 0, buflen);
+ flags |= HEADER_SEQNO64B; // use 64 bit by default
+ flags |= HEADER_EXTEND;
+ hdr->extend.version_u = htonl(IPERF_VERSION_MAJORHEX);
+ hdr->extend.version_l = htonl(IPERF_VERSION_MINORHEX);
+ hdr->extend.tos = htons(client->mTOS & 0xFF);
+ if (isBWSet(client) && (client->mAppRate > 0)) {
+ uint64_t bw = 0;
+ if ((client->mAppRateUnits == kRate_PPS) && (isFullDuplex(client) || isReverse(client) || (client->mMode != kTest_Normal))) {
+ bw = client->mAppRate * 8 * client->mBufLen;
+ } else {
+ bw = client->mAppRate;
+ }
+ hdr->extend.lRate = htonl((uint32_t)(bw));
+#ifdef HAVE_INT64_T
+ hdr->extend.uRate = htonl(((uint32_t)(bw >> 32)) << 8);
+#endif
+ } else {
+ hdr->extend.lRate = htonl(kDefault_UDPRate);
+ hdr->extend.uRate = 0x0;
+ }
+ len += sizeof(struct client_hdrext);
+ len += Settings_GenerateClientHdrV1(client, &hdr->base);
+ if (!isCompat(client) && (client->mMode != kTest_Normal)) {
+ flags |= HEADER_VERSION1;
+ if (client->mMode == kTest_DualTest)
+ flags |= RUN_NOW;
+ hdr->base.flags = htonl(flags);
+ }
+ /*
+ * set the default offset where underlying "inline" subsystems can write into the udp payload
+ */
+ if (isL2LengthCheck(client)) {
+ flags |= HEADER_UDPTESTS;
+ if (isL2LengthCheck(client)) {
+ upperflags |= HEADER_L2LENCHECK;
+ if (isIPV6(client))
+ upperflags |= HEADER_L2ETHPIPV6;
+ }
+ }
+ if (isIsochronous(client)) {
+ flags |= (HEADER_UDPTESTS | HEADER_EXTEND) ;
+ upperflags |= HEADER_ISOCH;
+ if (isFullDuplex(client) || isReverse(client)) {
+ upperflags |= HEADER_ISOCH_SETTINGS;
+ hdr->isoch_settings.FPSl = htonl((long)(client->mFPS));
+ hdr->isoch_settings.FPSu = htonl((long)((client->mFPS - (long)(client->mFPS)) * rMillion));
+ hdr->isoch_settings.Meanl = htonl((long)(client->mMean));
+ hdr->isoch_settings.Meanu = htonl((long)(((client->mMean) - (long)(client->mMean)) * rMillion));
+ hdr->isoch_settings.Variancel = htonl((long)(client->mVariance));
+ hdr->isoch_settings.Varianceu = htonl((long)((client->mVariance - (long)(client->mVariance)) * rMillion));
+ hdr->isoch_settings.BurstIPGl = htonl((long)(client->mBurstIPG));
+ hdr->isoch_settings.BurstIPGu = htonl((long)((client->mBurstIPG - (long)(client->mBurstIPG)) * rMillion));
+ len += sizeof(struct client_hdrext_isoch_settings);
+ }
+ }
+ if (isReverse(client) || isFullDuplex(client)) {
+ flags |= (HEADER_UDPTESTS | HEADER_VERSION2);
+ }
+ if (isNoUDPfin(client)) {
+ flags |= (HEADER_UDPTESTS | HEADER_EXTEND);
+ upperflags |= HEADER_NOUDPFIN;
+ }
+ if (isTripTime(client) || isFQPacing(client) || isTxStartTime(client)) {
+ flags |= HEADER_UDPTESTS;
+ if (isTripTime(client) || isTxStartTime(client)) {
+ hdr->start_fq.start_tv_sec = htonl(startTime.tv_sec);
+ hdr->start_fq.start_tv_usec = htonl(startTime.tv_usec);
+ if (isTripTime(client))
+ upperflags |= HEADER_TRIPTIME;
+ }
+ if (isFQPacing(client)) {
+ upperflags |= HEADER_FQRATESET;
+ hdr->start_fq.fqratel = htonl((uint32_t) client->mFQPacingRate);
+#ifdef HAVE_INT64_T
+ hdr->start_fq.fqrateu = htonl((uint32_t) (client->mFQPacingRate >> 32));
+#endif
+ }
+ }
+ // Write flags to header so the listener can determine the tests requested
+ hdr->extend.upperflags = htons(upperflags);
+ hdr->extend.lowerflags = htons(lowerflags);
+
+ // isoch payload is an enclave field between v 0.13 and v0.14
+ // so figure out if it's there now - UDP only
+ // will be filled in by the client write
+ if (isTripTime(client) || isFQPacing(client) || isIsochronous(client))
+ len += sizeof (struct isoch_payload);
+
+ if (len > 0) {
+ len += sizeof(struct UDP_datagram);
+ flags |= ((len << 1) & HEADER_KEYLEN_MASK) | HEADER_LEN_BIT;
+ }
+ hdr->base.flags = htonl(flags);
+#ifdef HAVE_THREAD_DEBUG
+ thread_debug("UDPflags = %X", ntohl(hdr->base.flags));
+#endif
+ } else { // TCP first write with test information
+ struct client_tcp_testhdr *hdr = static_cast<struct client_tcp_testhdr *>(testhdr);
+#ifdef HAVE_THREAD_DEBUG
+ thread_debug("Client header init size %d (%p)", sizeof(struct client_tcp_testhdr), (void *) hdr);
+#endif
+ if (isBounceBack(client)) {
+ flags = HEADER_BOUNCEBACK;
+ len = sizeof(struct bounceback_hdr);
+ } else {
+ int buflen = (client->mBufLen < (int) sizeof(struct client_tcp_testhdr)) ? client->mBufLen \
+ : sizeof(struct client_tcp_testhdr);
+ memset(hdr, 0, buflen);
+ flags |= HEADER_EXTEND;
+ hdr->extend.version_u = htonl(IPERF_VERSION_MAJORHEX);
+ hdr->extend.version_l = htonl(IPERF_VERSION_MINORHEX);
+ hdr->extend.tos = htons(client->mTOS & 0xFF);
+ if (isBWSet(client)) {
+ hdr->extend.lRate = htonl((uint32_t)(client->mAppRate));
+#ifdef HAVE_INT64_T
+ hdr->extend.uRate = htonl(((uint32_t)(client->mAppRate >> 32)) << 8);
+#endif
+ }
+ len += sizeof(struct client_hdrext);
+ len += Settings_GenerateClientHdrV1(client, &hdr->base);
+ if (!isCompat(client) && (client->mMode != kTest_Normal)) {
+ flags |= HEADER_VERSION1;
+ if (client->mMode == kTest_DualTest)
+ flags |= RUN_NOW;
+ }
+ if (isPeerVerDetect(client)) {
+ flags |= (HEADER_V2PEERDETECT | HEADER_VERSION2);
+ }
+ if (isTripTime(client) || isFQPacing(client) || isIsochronous(client) || isTxStartTime(client)) {
+ hdr->start_fq.start_tv_sec = htonl(startTime.tv_sec);
+ hdr->start_fq.start_tv_usec = htonl(startTime.tv_usec);
+ hdr->start_fq.fqratel = htonl((uint32_t) client->mFQPacingRate);
+#ifdef HAVE_INT64_T
+ hdr->start_fq.fqrateu = htonl((uint32_t) (client->mFQPacingRate >> 32));
+#endif
+ len += sizeof(struct client_hdrext_starttime_fq);
+ // Set flags on
+ if (isTripTime(client)) {
+ upperflags |= HEADER_TRIPTIME;
+ }
+ if (isFQPacing(client)) {
+ upperflags |= HEADER_FQRATESET;
+ }
+ }
+#if HAVE_DECL_TCP_NOTSENT_LOWAT
+ if (isWritePrefetch(client) && (isReverse(client) || isFullDuplex(client))) {
+ upperflags |= HEADER_WRITEPREFETCH;
+ hdr->extend.TCPWritePrefetch = htonl((long)client->mWritePrefetch);
+ }
+#endif
+#if HAVE_DECL_TCP_QUICKACK
+ if (isTcpQuickAck(client) && (!isReverse(client) || isFullDuplex(client))) {
+ upperflags |= HEADER_TCPQUICKACK;
+ }
+#endif
+ if (isIsochronous(client) || isPeriodicBurst(client)) {
+ if (isPeriodicBurst(client)) {
+ upperflags |= HEADER_PERIODICBURST; // overload the isoch settings
+ } else {
+ upperflags |= HEADER_ISOCH;
+ }
+ if (isFullDuplex(client) || isReverse(client) || isPeriodicBurst(client)) {
+ hdr->isoch_settings.FPSl = htonl((long)client->mFPS);
+ hdr->isoch_settings.FPSu = htonl((long)((client->mFPS - (long)(client->mFPS)) * rMillion));
+ hdr->isoch_settings.Variancel = htonl((long)(client->mVariance));
+ hdr->isoch_settings.Varianceu = htonl((long)((client->mVariance - (long)(client->mVariance)) * rMillion));
+ if (!isPeriodicBurst(client)) {
+ hdr->isoch_settings.Meanl = htonl((long)(client->mMean));
+ hdr->isoch_settings.Meanu = htonl((long)(((client->mMean) - (long)(client->mMean)) * rMillion));
+ hdr->isoch_settings.BurstIPGl = htonl((long)client->mBurstIPG);
+ hdr->isoch_settings.BurstIPGu = htonl(((long)(client->mBurstIPG) - (long)client->mBurstIPG * rMillion));
+ } else {
+ hdr->isoch_settings.Meanl = htonl((long)(client->mBurstSize));
+ }
+ len += sizeof(struct client_hdrext_isoch_settings);
+ }
+ }
+ if (isReverse(client) || isFullDuplex(client)) {
+ flags |= HEADER_VERSION2;
+ }
+ hdr->extend.upperflags = htons(upperflags);
+ hdr->extend.lowerflags = htons(lowerflags);
+ if (len > 0) {
+ flags |= HEADER_LEN_BIT;
+ int keylen = 0;
+ if (!isServerReverse(client) && isPermitKey(client) && (client->mPermitKey[0] != '\0')) {
+ keylen = static_cast<int>(strnlen(client->mPermitKey, MAX_PERMITKEY_LEN));
+ flags |= HEADER_KEYCHECK;
+ struct permitKey *thiskey = reinterpret_cast<struct permitKey *>(static_cast<char *>(testhdr) + len);
+ thiskey->length = htons((uint16_t)keylen);
+ memcpy(thiskey->value, client->mPermitKey, keylen);
+ len += sizeof(thiskey->length);
+ }
+ flags |= ((len << 1) & HEADER_KEYLEN_MASK); // this is the key value offset passed to the server
+ len += keylen;
+ }
+ }
+ hdr->base.flags = htonl(flags);
+ }
+ return (len);
+}
+
+int Settings_ClientTestHdrLen (uint32_t flags, struct thread_Settings *inSettings) {
+ int peeklen = 0;
+ if ((flags & HEADER_VERSION1) || (flags & HEADER_VERSION2) || (flags & HEADER_EXTEND) || isPermitKey(inSettings)) {
+ if (flags & HEADER_LEN_BIT) {
+ peeklen = static_cast<int>((flags & HEADER_LEN_MASK) >> 1);
+ if (peeklen > MAX_HEADER_LEN) {
+ fprintf(stderr, "WARN: header of %d length too large\n", peeklen);
+ peeklen = -1;
+ }
+ } else {
+ if (flags & (HEADER_VERSION1 | HEADER_EXTEND)) {
+ peeklen = sizeof(struct client_hdr_v1);
+ }
+ if (flags & (HEADER_VERSION2 | HEADER_EXTEND)) {
+ peeklen += sizeof(struct client_hdrext);
+ }
+ }
+ }
+ return peeklen;
+}