diff options
Diffstat (limited to 'src/Settings.cpp')
-rw-r--r-- | src/Settings.cpp | 2577 |
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, ¬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<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; +} |