VirtualBox

source: vbox/trunk/src/VBox/ValidationKit/utils/network/NetPerf.cpp@ 72293

Last change on this file since 72293 was 70404, checked in by vboxsync, 7 years ago

NetPerf: Added --verbose option.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 65.3 KB
Line 
1/* $Id: NetPerf.cpp 70404 2018-01-01 15:30:25Z vboxsync $ */
2/** @file
3 * NetPerf - Network Performance Benchmark.
4 */
5
6/*
7 * Copyright (C) 2010-2017 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#include <iprt/asm.h>
32#include <iprt/assert.h>
33#include <iprt/ctype.h>
34#include <iprt/err.h>
35#include <iprt/getopt.h>
36#include <iprt/initterm.h>
37#include <iprt/mem.h>
38#include <iprt/message.h>
39#include <iprt/path.h>
40#include <iprt/param.h>
41#include <iprt/process.h>
42#include <iprt/rand.h>
43#include <iprt/stream.h>
44#include <iprt/string.h>
45#include <iprt/tcp.h>
46#include <iprt/thread.h>
47#include <iprt/test.h>
48#include <iprt/time.h>
49#include <iprt/timer.h>
50
51
52/*********************************************************************************************************************************
53* Defined Constants And Macros *
54*********************************************************************************************************************************/
55/** Default TCP port (update help text if you change this) */
56#define NETPERF_DEFAULT_PORT 5002
57
58/** Default TCP packet size (bytes) */
59#define NETPERF_DEFAULT_PKT_SIZE_THROUGHPUT 8192
60/** Default TCP packet size (bytes) */
61#define NETPERF_DEFAULT_PKT_SIZE_LATENCY 1024
62/** Maximum packet size possible (bytes). */
63#define NETPERF_MAX_PKT_SIZE _1M
64/** Minimum packet size possible (bytes). */
65#define NETPERF_MIN_PKT_SIZE sizeof(NETPERFHDR)
66
67/** Default timeout in (seconds) */
68#define NETPERF_DEFAULT_TIMEOUT 10
69/** Maximum timeout possible (seconds). */
70#define NETPERF_MAX_TIMEOUT 3600 /* 1h */
71/** Minimum timeout possible (seconds). */
72#define NETPERF_MIN_TIMEOUT 1
73
74/** The default warmup time (ms). */
75#define NETPERF_DEFAULT_WARMUP 1000 /* 1s */
76/** The maxium warmup time (ms). */
77#define NETPERF_MAX_WARMUP 60000 /* 60s */
78/** The minimum warmup time (ms). */
79#define NETPERF_MIN_WARMUP 1000 /* 1s */
80
81/** The default cool down time (ms). */
82#define NETPERF_DEFAULT_COOL_DOWN 1000 /* 1s */
83/** The maxium cool down time (ms). */
84#define NETPERF_MAX_COOL_DOWN 60000 /* 60s */
85/** The minimum cool down time (ms). */
86#define NETPERF_MIN_COOL_DOWN 1000 /* 1s */
87
88/** The length of the length prefix used when submitting parameters and
89 * results. */
90#define NETPERF_LEN_PREFIX 4
91
92
93/*********************************************************************************************************************************
94* Structures and Typedefs *
95*********************************************************************************************************************************/
96typedef enum NETPERFPROTO
97{
98 NETPERFPROTO_INVALID = 0,
99 NETPERFPROTO_TCP
100 //NETPERFPROTO_UDP
101} NETPERFPROTO;
102
103/**
104 * What kind of test we're performing.
105 */
106typedef enum NETPERFMODE
107{
108 NETPERFMODE_INVALID = 0,
109 /** Latency of a symmetric packet exchange. */
110 NETPERFMODE_LATENCY,
111 /** Separate throughput measurements for each direction. */
112 NETPERFMODE_THROUGHPUT,
113 /** Transmit throughput. */
114 NETPERFMODE_THROUGHPUT_XMIT,
115 /** Transmit throughput. */
116 NETPERFMODE_THROUGHPUT_RECV
117} NETPERFMODE;
118
119/**
120 * Statistics.
121 */
122typedef struct NETPERFSTATS
123{
124 uint64_t cTx;
125 uint64_t cRx;
126 uint64_t cEchos;
127 uint64_t cErrors;
128 uint64_t cNsElapsed;
129} NETPERFSTATS;
130
131/**
132 * Settings & a little bit of state.
133 */
134typedef struct NETPERFPARAMS
135{
136 /** @name Static settings
137 * @{ */
138 /** The TCP port number. */
139 uint32_t uPort;
140 /** Client: Use server statistcs. */
141 bool fServerStats;
142 /** Server: Quit after the first client. */
143 bool fSingleClient;
144 /** @} */
145
146 /** @name Dynamic settings
147 * @{ */
148 /** Disable send packet coalescing. */
149 bool fNoDelay;
150 /** Detect broken payloads. */
151 bool fCheckData;
152 /** The test mode. */
153 NETPERFMODE enmMode;
154 /** The number of seconds to run each of the test steps. */
155 uint32_t cSecTimeout;
156 /** Number of millisecond to spend warning up before testing. */
157 uint32_t cMsWarmup;
158 /** Number of millisecond to spend cooling down after the testing. */
159 uint32_t cMsCoolDown;
160 /** The packet size. */
161 uint32_t cbPacket;
162 /** @} */
163
164 /** @name State
165 * @{ */
166 RTSOCKET hSocket;
167 /** @} */
168} NETPERFPARAMS;
169
170/**
171 * Packet header used in tests.
172 *
173 * Need to indicate when we've timed out and it's time to reverse the roles or
174 * stop testing.
175 */
176typedef struct NETPERFHDR
177{
178 /** Magic value (little endian). */
179 uint32_t u32Magic;
180 /** State value. */
181 uint32_t u32State;
182 /** Sequence number (little endian). */
183 uint32_t u32Seq;
184 /** Reserved, must be zero. */
185 uint32_t u32Reserved;
186} NETPERFHDR;
187
188/** Magic value for NETPERFHDR::u32Magic. */
189#define NETPERFHDR_MAGIC UINT32_C(0xfeedf00d)
190
191/** @name Packet State (NETPERF::u32Magic)
192 * @{ */
193/** Warm up. */
194#define NETPERFHDR_WARMUP UINT32_C(0x0c0ffe01)
195/** The clock is running. */
196#define NETPERFHDR_TESTING UINT32_C(0x0c0ffe02)
197/** Stop the clock but continue the package flow. */
198#define NETPERFHDR_COOL_DOWN UINT32_C(0x0c0ffe03)
199/** Done, stop the clock if not done already and reply with results. */
200#define NETPERFHDR_DONE UINT32_C(0x0c0ffe04)
201/** @} */
202
203
204/*********************************************************************************************************************************
205* Global Variables *
206*********************************************************************************************************************************/
207/** Connection start/identifier to make sure other end is NetPerf. */
208static const char g_ConnectStart[] = "yo! waaazzzzzaaaaup dude?";
209/** Start of parameters proposal made by the client. */
210static const char g_szStartParams[] = "deal?";
211/** All okay to start test */
212static const char g_szAck[] = "okay!";
213/** Negative. */
214static const char g_szNegative[] = "nope!";
215AssertCompile(sizeof(g_szAck) == sizeof(g_szNegative));
216/** Start of statistics. */
217static const char g_szStartStats[] = "dude, stats";
218
219/** Command line parameters */
220static const RTGETOPTDEF g_aCmdOptions[] =
221{
222 { "--server", 's', RTGETOPT_REQ_NOTHING },
223 { "--client", 'c', RTGETOPT_REQ_STRING },
224 { "--interval", 'i', RTGETOPT_REQ_UINT32 },
225 { "--port", 'p', RTGETOPT_REQ_UINT32 },
226 { "--len", 'l', RTGETOPT_REQ_UINT32 },
227 { "--nodelay", 'N', RTGETOPT_REQ_NOTHING },
228 { "--mode", 'm', RTGETOPT_REQ_STRING },
229 { "--warmpup", 'w', RTGETOPT_REQ_UINT32 },
230 { "--cool-down", 'W', RTGETOPT_REQ_UINT32 },
231 { "--server-stats", 'S', RTGETOPT_REQ_NOTHING },
232 { "--single-client", '1', RTGETOPT_REQ_NOTHING },
233 { "--daemonize", 'd', RTGETOPT_REQ_NOTHING },
234 { "--daemonized", 'D', RTGETOPT_REQ_NOTHING },
235 { "--check-data", 'C', RTGETOPT_REQ_NOTHING },
236 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
237 { "--help", 'h', RTGETOPT_REQ_NOTHING } /* for Usage() */
238};
239
240/** The test handle. */
241static RTTEST g_hTest;
242/** Verbosity level. */
243static uint32_t g_uVerbosity = 0;
244
245
246
247static void Usage(PRTSTREAM pStrm)
248{
249 char szExec[RTPATH_MAX];
250 RTStrmPrintf(pStrm, "usage: %s <-s|-c <host>> [options]\n",
251 RTPathFilename(RTProcGetExecutablePath(szExec, sizeof(szExec))));
252 RTStrmPrintf(pStrm, "\n");
253 RTStrmPrintf(pStrm, "options: \n");
254
255
256 for (unsigned i = 0; i < RT_ELEMENTS(g_aCmdOptions); i++)
257 {
258 const char *pszHelp;
259 switch (g_aCmdOptions[i].iShort)
260 {
261 case 'h':
262 pszHelp = "Displays this help and exit";
263 break;
264 case 's':
265 pszHelp = "Run in server mode, waiting for clients (default)";
266 break;
267 case 'c':
268 pszHelp = "Run in client mode, connecting to <host>";
269 break;
270 case 'i':
271 pszHelp = "Interval in seconds to run the test (default " RT_XSTR(NETPERF_DEFAULT_TIMEOUT) " s)";
272 break;
273 case 'p':
274 pszHelp = "Server port to listen/connect to (default " RT_XSTR(NETPERF_DEFAULT_PORT) ")";
275 break;
276 case 'l':
277 pszHelp = "Packet size in bytes (defaults to " RT_XSTR(NETPERF_DEFAULT_PKT_SIZE_LATENCY)
278 " for latency and " RT_XSTR(NETPERF_DEFAULT_PKT_SIZE_THROUGHPUT) " for throughput)";
279 break;
280 case 'm':
281 pszHelp = "Test mode: latency (default), throughput, throughput-xmit or throughput-recv";
282 break;
283 case 'N':
284 pszHelp = "Set TCP no delay, disabling Nagle's algorithm";
285 break;
286 case 'S':
287 pszHelp = "Report server stats, ignored if server";
288 break;
289 case '1':
290 pszHelp = "Stop the server after the first client";
291 break;
292 case 'd':
293 pszHelp = "Daemonize if server, ignored if client";
294 break;
295 case 'D':
296 continue; /* internal */
297 case 'w':
298 pszHelp = "Warmup time, in milliseconds (default " RT_XSTR(NETPERF_DEFAULT_WARMPUP) " ms)";
299 break;
300 case 'W':
301 pszHelp = "Cool down time, in milliseconds (default " RT_XSTR(NETPERF_DEFAULT_COOL_DOWN) " ms)";
302 break;
303 case 'C':
304 pszHelp = "Check payload data at the receiving end";
305 break;
306 case 'v':
307 pszHelp = "Verbose execution.";
308 break;
309 default:
310 pszHelp = "Option undocumented";
311 break;
312 }
313 char szOpt[256];
314 RTStrPrintf(szOpt, sizeof(szOpt), "%s, -%c", g_aCmdOptions[i].pszLong, g_aCmdOptions[i].iShort);
315 RTStrmPrintf(pStrm, " %-20s%s\n", szOpt, pszHelp);
316 }
317}
318
319/**
320 * Timer callback employed to set the stop indicator.
321 *
322 * This is used both by the client and server side.
323 *
324 * @param hTimer The timer, ignored.
325 * @param pvUser Pointer to the stop variable.
326 * @param iTick The tick, ignored.
327 */
328static DECLCALLBACK(void) netperfStopTimerCallback(RTTIMERLR hTimer, void *pvUser, uint64_t iTick)
329{
330 bool volatile *pfStop = (bool volatile *)pvUser;
331 if (g_uVerbosity > 0)
332 RTPrintf("Time's Up!\n");
333 ASMAtomicWriteBool(pfStop, true);
334 NOREF(hTimer); NOREF(iTick);
335}
336
337/**
338 * Sends a statistics packet to our peer.
339 *
340 * @returns IPRT status code.
341 * @param pStats The stats to send.
342 * @param hSocket The TCP socket to send them to.
343 */
344static int netperfSendStats(NETPERFSTATS const *pStats, RTSOCKET hSocket)
345{
346 char szBuf[256 + NETPERF_LEN_PREFIX];
347 size_t cch = RTStrPrintf(&szBuf[NETPERF_LEN_PREFIX], sizeof(szBuf) - NETPERF_LEN_PREFIX,
348 "%s:%llu:%llu:%llu:%llu:%llu",
349 g_szStartStats,
350 pStats->cTx,
351 pStats->cRx,
352 pStats->cEchos,
353 pStats->cErrors,
354 pStats->cNsElapsed);
355
356 RTStrPrintf(szBuf, NETPERF_LEN_PREFIX + 1, "%0*u", NETPERF_LEN_PREFIX, cch);
357 szBuf[NETPERF_LEN_PREFIX] = g_szStartStats[0];
358 Assert(strlen(szBuf) == cch + NETPERF_LEN_PREFIX);
359
360 int rc = RTTcpWrite(hSocket, szBuf, cch + NETPERF_LEN_PREFIX);
361 if (RT_FAILURE(rc))
362 return RTTestIFailedRc(rc, "stats: Failed to send stats: %Rrc\n", rc);
363
364 /*
365 * Wait for ACK.
366 */
367 rc = RTTcpRead(hSocket, szBuf, sizeof(g_szAck) - 1, NULL);
368 if (RT_FAILURE(rc))
369 return RTTestIFailedRc(rc, "stats: failed to write stats: %Rrc\n", rc);
370 szBuf[sizeof(g_szAck) - 1] = '\0';
371 if (!strcmp(szBuf, g_szNegative))
372 return RTTestIFailedRc(rc, "stats: client failed to parse them\n");
373 if (strcmp(szBuf, g_szAck))
374 return RTTestIFailedRc(rc, "stats: got '%s' in instead of ack/nack\n", szBuf);
375
376 return VINF_SUCCESS;
377}
378
379/**
380 * Receives a statistics packet from our peer.
381 *
382 * @returns IPRT status code. Error signalled.
383 * @param pStats Where to receive the stats.
384 * @param hSocket The TCP socket to recevie them from.
385 */
386static int netperfRecvStats(NETPERFSTATS *pStats, RTSOCKET hSocket)
387{
388 /*
389 * Read the stats message.
390 */
391 /* the length prefix */
392 char szBuf[256 + NETPERF_LEN_PREFIX];
393 int rc = RTTcpRead(hSocket, szBuf, NETPERF_LEN_PREFIX, NULL);
394 if (RT_FAILURE(rc))
395 return RTTestIFailedRc(rc, "stats: failed to read stats prefix: %Rrc\n", rc);
396
397 szBuf[NETPERF_LEN_PREFIX] = '\0';
398 uint32_t cch;
399 rc = RTStrToUInt32Full(szBuf, 10, &cch);
400 if (rc != VINF_SUCCESS)
401 return RTTestIFailedRc(RT_SUCCESS(rc) ? -rc : rc, "stats: bad stat length prefix: '%s' - %Rrc\n", szBuf, rc);
402 if (cch >= sizeof(szBuf))
403 return RTTestIFailedRc(VERR_BUFFER_OVERFLOW, "stats: too large: %u bytes\n", cch);
404
405 /* the actual message */
406 rc = RTTcpRead(hSocket, szBuf, cch, NULL);
407 if (RT_FAILURE(rc))
408 return RTTestIFailedRc(rc, "failed to read stats: %Rrc\n", rc);
409 szBuf[cch] = '\0';
410
411 /*
412 * Validate the message header.
413 */
414 if ( strncmp(szBuf, g_szStartStats, sizeof(g_szStartStats) - 1)
415 || szBuf[sizeof(g_szStartStats) - 1] != ':')
416 return RTTestIFailedRc(VERR_NET_PROTOCOL_ERROR, "stats: invalid packet start: '%s'\n", szBuf);
417 char *pszCur = &szBuf[sizeof(g_szStartStats)];
418
419 /*
420 * Parse it.
421 */
422 static const char * const s_apszNames[] =
423 {
424 "cTx", "cRx", "cEchos", "cErrors", "cNsElapsed"
425 };
426 uint64_t *apu64[RT_ELEMENTS(s_apszNames)] =
427 {
428 &pStats->cTx,
429 &pStats->cRx,
430 &pStats->cEchos,
431 &pStats->cErrors,
432 &pStats->cNsElapsed
433 };
434
435 for (unsigned i = 0; i < RT_ELEMENTS(apu64); i++)
436 {
437 if (!pszCur)
438 return RTTestIFailedRc(VERR_PARSE_ERROR, "stats: missing %s\n", s_apszNames[i]);
439
440 char *pszNext = strchr(pszCur, ':');
441 if (pszNext)
442 *pszNext++ = '\0';
443 rc = RTStrToUInt64Full(pszCur, 10, apu64[i]);
444 if (rc != VINF_SUCCESS)
445 return RTTestIFailedRc(RT_SUCCESS(rc) ? -rc : rc, "stats: bad value for %s: '%s' - %Rrc\n",
446 s_apszNames[i], pszCur, rc);
447
448 pszCur = pszNext;
449 }
450
451 if (pszCur)
452 return RTTestIFailedRc(VERR_PARSE_ERROR, "stats: Unparsed data: '%s'\n", pszCur);
453
454 /*
455 * Send ACK.
456 */
457 rc = RTTcpWrite(hSocket, g_szAck, sizeof(g_szAck) - 1);
458 if (RT_FAILURE(rc))
459 return RTTestIFailedRc(rc, "stats: failed to write ack: %Rrc\n", rc);
460
461 return VINF_SUCCESS;
462}
463
464/**
465 * TCP Throughput: Print the statistics.
466 *
467 * @param pSendStats Send stats.
468 * @param pRecvStats Receive stats.
469 * @param cbPacket Packet size.
470 */
471static void netperfPrintThroughputStats(NETPERFSTATS const *pSendStats, NETPERFSTATS const *pRecvStats, uint32_t cbPacket)
472{
473 RTTestIValue("Packet size", cbPacket, RTTESTUNIT_BYTES);
474
475 if (pSendStats)
476 {
477 double rdSecElapsed = (double)pSendStats->cNsElapsed / 1000000000.0;
478 RTTestIValue("Sends", pSendStats->cTx, RTTESTUNIT_PACKETS);
479 RTTestIValue("Send Interval", pSendStats->cNsElapsed, RTTESTUNIT_NS);
480 RTTestIValue("Send Throughput", (uint64_t)(cbPacket * pSendStats->cTx / rdSecElapsed), RTTESTUNIT_BYTES_PER_SEC);
481 RTTestIValue("Send Rate", (uint64_t)(pSendStats->cTx / rdSecElapsed), RTTESTUNIT_PACKETS_PER_SEC);
482 RTTestIValue("Send Latency", (uint64_t)(rdSecElapsed / pSendStats->cTx * 1000000000.0), RTTESTUNIT_NS_PER_PACKET);
483 }
484
485 if (pRecvStats)
486 {
487 double rdSecElapsed = (double)pRecvStats->cNsElapsed / 1000000000.0;
488 RTTestIValue("Receives", pRecvStats->cRx, RTTESTUNIT_PACKETS);
489 RTTestIValue("Receive Interval", pRecvStats->cNsElapsed, RTTESTUNIT_NS);
490 RTTestIValue("Receive Throughput", (uint64_t)(cbPacket * pRecvStats->cRx / rdSecElapsed), RTTESTUNIT_BYTES_PER_SEC);
491 RTTestIValue("Receive Rate", (uint64_t)(pRecvStats->cRx / rdSecElapsed), RTTESTUNIT_PACKETS_PER_SEC);
492 RTTestIValue("Receive Latency", (uint64_t)(rdSecElapsed / pRecvStats->cRx * 1000000000.0), RTTESTUNIT_NS_PER_PACKET);
493 }
494}
495
496/**
497 * TCP Throughput: Send data to the other party.
498 *
499 * @returns IPRT status code.
500 * @param pParams The TCP parameters block.
501 * @param pBuf The buffer we're using when sending.
502 * @param pSendStats Where to return the statistics.
503 */
504static int netperfTCPThroughputSend(NETPERFPARAMS const *pParams, NETPERFHDR *pBuf, NETPERFSTATS *pSendStats)
505{
506 RT_ZERO(*pSendStats);
507
508 /*
509 * Create the timer
510 */
511 RTTIMERLR hTimer;
512 bool volatile fStop = false;
513 int rc = RTTimerLRCreateEx(&hTimer, 0 /* nsec */, RTTIMER_FLAGS_CPU_ANY, netperfStopTimerCallback, (void *)&fStop);
514 if (RT_SUCCESS(rc))
515 {
516 uint32_t u32Seq = 0;
517
518 RT_BZERO(pBuf, pParams->cbPacket);
519 pBuf->u32Magic = RT_H2LE_U32_C(NETPERFHDR_MAGIC);
520 pBuf->u32State = 0;
521 pBuf->u32Seq = 0;
522 pBuf->u32Reserved = 0;
523
524 /*
525 * Warm up.
526 */
527 if (g_uVerbosity > 0)
528 RTPrintf("Warmup...\n");
529 pBuf->u32State = RT_H2LE_U32_C(NETPERFHDR_WARMUP);
530 rc = RTTimerLRStart(hTimer, pParams->cMsWarmup * UINT64_C(1000000) /* nsec */);
531 if (RT_SUCCESS(rc))
532 {
533 while (!fStop)
534 {
535 u32Seq++;
536 pBuf->u32Seq = RT_H2LE_U32(u32Seq);
537 rc = RTTcpWrite(pParams->hSocket, pBuf, pParams->cbPacket);
538 if (RT_FAILURE(rc))
539 {
540 RTTestIFailed("RTTcpWrite/warmup: %Rrc\n", rc);
541 break;
542 }
543 }
544 }
545 else
546 RTTestIFailed("RTTimerLRStart/warmup: %Rrc\n", rc);
547
548 /*
549 * The real thing.
550 */
551 if (RT_SUCCESS(rc))
552 {
553 if (g_uVerbosity > 0)
554 RTPrintf("The real thing...\n");
555 pBuf->u32State = RT_H2LE_U32_C(NETPERFHDR_TESTING);
556 fStop = false;
557 rc = RTTimerLRStart(hTimer, pParams->cSecTimeout * UINT64_C(1000000000) /* nsec */);
558 if (RT_SUCCESS(rc))
559 {
560 uint64_t u64StartTS = RTTimeNanoTS();
561 while (!fStop)
562 {
563 u32Seq++;
564 pBuf->u32Seq = RT_H2LE_U32(u32Seq);
565 rc = RTTcpWrite(pParams->hSocket, pBuf, pParams->cbPacket);
566 if (RT_FAILURE(rc))
567 {
568 RTTestIFailed("RTTcpWrite/testing: %Rrc\n", rc);
569 break;
570 }
571 pSendStats->cTx++;
572 }
573 pSendStats->cNsElapsed = RTTimeNanoTS() - u64StartTS;
574 }
575 else
576 RTTestIFailed("RTTimerLRStart/testing: %Rrc\n", rc);
577 }
578
579 /*
580 * Cool down.
581 */
582 if (RT_SUCCESS(rc))
583 {
584 if (g_uVerbosity > 0)
585 RTPrintf("Cool down...\n");
586 pBuf->u32State = RT_H2LE_U32_C(NETPERFHDR_COOL_DOWN);
587 fStop = false;
588 rc = RTTimerLRStart(hTimer, pParams->cMsCoolDown * UINT64_C(1000000) /* nsec */);
589 if (RT_SUCCESS(rc))
590 {
591 while (!fStop)
592 {
593 u32Seq++;
594 pBuf->u32Seq = RT_H2LE_U32(u32Seq);
595 rc = RTTcpWrite(pParams->hSocket, pBuf, pParams->cbPacket);
596 if (RT_FAILURE(rc))
597 {
598 RTTestIFailed("RTTcpWrite/cool down: %Rrc\n", rc);
599 break;
600 }
601 }
602 }
603 else
604 RTTestIFailed("RTTimerLRStart/testing: %Rrc\n", rc);
605 }
606
607 /*
608 * Send DONE packet.
609 */
610 if (g_uVerbosity > 0)
611 RTPrintf("Done\n");
612 if (RT_SUCCESS(rc))
613 {
614 u32Seq++;
615 pBuf->u32Seq = RT_H2LE_U32(u32Seq);
616 pBuf->u32State = RT_H2LE_U32_C(NETPERFHDR_DONE);
617 rc = RTTcpWrite(pParams->hSocket, pBuf, pParams->cbPacket);
618 if (RT_FAILURE(rc))
619 RTTestIFailed("RTTcpWrite/done: %Rrc\n", rc);
620 }
621
622 RTTimerLRDestroy(hTimer);
623 }
624 else
625 RTTestIFailed("Failed to create timer object: %Rrc\n", rc);
626 return rc;
627}
628
629
630/**
631 * TCP Throughput: Receive data from the other party.
632 *
633 * @returns IPRT status code.
634 * @param pParams The TCP parameters block.
635 * @param pBuf The buffer we're using when sending.
636 * @param pStats Where to return the statistics.
637 */
638static int netperfTCPThroughputRecv(NETPERFPARAMS const *pParams, NETPERFHDR *pBuf, NETPERFSTATS *pStats)
639{
640 RT_ZERO(*pStats);
641
642 int rc;
643 uint32_t u32Seq = 0;
644 uint64_t cRx = 0;
645 uint64_t u64StartTS = 0;
646 uint32_t uState = RT_H2LE_U32_C(NETPERFHDR_WARMUP);
647
648 for (;;)
649 {
650 rc = RTTcpRead(pParams->hSocket, pBuf, pParams->cbPacket, NULL);
651 if (RT_FAILURE(rc))
652 {
653 pStats->cErrors++;
654 RTTestIFailed("RTTcpRead failed: %Rrc\n", rc);
655 break;
656 }
657 if (RT_UNLIKELY( pBuf->u32Magic != RT_H2LE_U32_C(NETPERFHDR_MAGIC)
658 || pBuf->u32Reserved != 0))
659 {
660 pStats->cErrors++;
661 RTTestIFailed("Invalid magic or reserved field value: %#x %#x\n", RT_H2LE_U32(pBuf->u32Magic), RT_H2LE_U32(pBuf->u32Reserved));
662 rc = VERR_INVALID_MAGIC;
663 break;
664 }
665
666 u32Seq += 1;
667 if (RT_UNLIKELY(pBuf->u32Seq != RT_H2LE_U32(u32Seq)))
668 {
669 pStats->cErrors++;
670 RTTestIFailed("Out of sequence: got %#x, expected %#x\n", RT_H2LE_U32(pBuf->u32Seq), u32Seq);
671 rc = VERR_WRONG_ORDER;
672 break;
673 }
674
675 if (pParams->fCheckData && uState == RT_H2LE_U32_C(NETPERFHDR_TESTING))
676 {
677 unsigned i = sizeof(NETPERFHDR);
678 for (;i < pParams->cbPacket; ++i)
679 if (((unsigned char *)pBuf)[i])
680 break;
681 if (i != pParams->cbPacket)
682 {
683 pStats->cErrors++;
684 RTTestIFailed("Broken payload: at %#x got %#x, expected %#x\n", i, ((unsigned char *)pBuf)[i], 0);
685 rc = VERR_NOT_EQUAL;
686 break;
687 }
688 }
689 if (RT_LIKELY(pBuf->u32State == uState))
690 cRx++;
691 /*
692 * Validate and act on switch state.
693 */
694 else if ( uState == RT_H2LE_U32_C(NETPERFHDR_WARMUP)
695 && pBuf->u32State == RT_H2LE_U32_C(NETPERFHDR_TESTING))
696 {
697 cRx = 0;
698 u64StartTS = RTTimeNanoTS();
699 uState = pBuf->u32State;
700 }
701 else if ( uState == RT_H2LE_U32_C(NETPERFHDR_TESTING)
702 && ( pBuf->u32State == RT_H2LE_U32_C(NETPERFHDR_COOL_DOWN)
703 || pBuf->u32State == RT_H2LE_U32_C(NETPERFHDR_DONE)) )
704 {
705 pStats->cNsElapsed = RTTimeNanoTS() - u64StartTS;
706 pStats->cRx = cRx + 1;
707 uState = pBuf->u32State;
708 if (uState == RT_H2LE_U32_C(NETPERFHDR_DONE))
709 break;
710 }
711 else if ( uState == RT_H2LE_U32_C(NETPERFHDR_COOL_DOWN)
712 && pBuf->u32State == RT_H2LE_U32_C(NETPERFHDR_DONE))
713 {
714 uState = pBuf->u32State;
715 break;
716 }
717 else
718 {
719 pStats->cErrors++;
720 RTTestIFailed("Protocol error: invalid state transition %#x -> %#x\n",
721 RT_LE2H_U32(uState), RT_LE2H_U32(pBuf->u32State));
722 rc = VERR_INVALID_MAGIC;
723 break;
724 }
725 }
726
727 AssertReturn(uState == RT_H2LE_U32_C(NETPERFHDR_DONE) || RT_FAILURE(rc), VERR_INVALID_STATE);
728 return rc;
729}
730
731
732/**
733 * Prints the statistics for the latency test.
734 *
735 * @param pStats The statistics.
736 * @param cbPacket The packet size in bytes.
737 */
738static void netperfPrintLatencyStats(NETPERFSTATS const *pStats, uint32_t cbPacket)
739{
740 double rdSecElapsed = (double)pStats->cNsElapsed / 1000000000.0;
741 RTTestIValue("Transmitted", pStats->cTx, RTTESTUNIT_PACKETS);
742 RTTestIValue("Successful echos", pStats->cEchos, RTTESTUNIT_PACKETS);
743 RTTestIValue("Errors", pStats->cErrors, RTTESTUNIT_PACKETS);
744 RTTestIValue("Interval", pStats->cNsElapsed, RTTESTUNIT_NS);
745 RTTestIValue("Packet size", cbPacket, RTTESTUNIT_BYTES);
746 RTTestIValue("Average rate", (uint64_t)(pStats->cEchos / rdSecElapsed), RTTESTUNIT_PACKETS_PER_SEC);
747 RTTestIValue("Average throughput", (uint64_t)(cbPacket * pStats->cEchos / rdSecElapsed), RTTESTUNIT_BYTES_PER_SEC);
748 RTTestIValue("Average latency", (uint64_t)(rdSecElapsed / pStats->cEchos * 1000000000.0), RTTESTUNIT_NS_PER_ROUND_TRIP);
749 RTTestISubDone();
750}
751
752
753/**
754 * NETPERFMODE -> string.
755 *
756 * @returns readonly string.
757 * @param enmMode The mode.
758 */
759static const char *netperfModeToString(NETPERFMODE enmMode)
760{
761 switch (enmMode)
762 {
763 case NETPERFMODE_LATENCY: return "latency";
764 case NETPERFMODE_THROUGHPUT: return "throughput";
765 case NETPERFMODE_THROUGHPUT_XMIT: return "throughput-xmit";
766 case NETPERFMODE_THROUGHPUT_RECV: return "throughput-recv";
767 default: AssertFailed(); return "internal-error";
768 }
769}
770
771/**
772 * String -> NETPERFMODE.
773 *
774 * @returns The corresponding NETPERFMODE, NETPERFMODE_INVALID on failure.
775 * @param pszMode The mode string.
776 */
777static NETPERFMODE netperfModeFromString(const char *pszMode)
778{
779 if (!strcmp(pszMode, "latency"))
780 return NETPERFMODE_LATENCY;
781 if ( !strcmp(pszMode, "throughput")
782 || !strcmp(pszMode, "thruput") )
783 return NETPERFMODE_THROUGHPUT;
784 if ( !strcmp(pszMode, "throughput-xmit")
785 || !strcmp(pszMode, "thruput-xmit")
786 || !strcmp(pszMode, "xmit") )
787 return NETPERFMODE_THROUGHPUT_XMIT;
788 if ( !strcmp(pszMode, "throughput-recv")
789 || !strcmp(pszMode, "thruput-recv")
790 || !strcmp(pszMode, "recv") )
791 return NETPERFMODE_THROUGHPUT_RECV;
792 return NETPERFMODE_INVALID;
793}
794
795
796
797
798
799/**
800 * TCP Server: Throughput test.
801 *
802 * @returns IPRT status code.
803 * @param pParams The parameters to use for this test.
804 */
805static int netperfTCPServerDoThroughput(NETPERFPARAMS const *pParams)
806{
807 /*
808 * Allocate the buffer.
809 */
810 NETPERFHDR *pBuf = (NETPERFHDR *)RTMemAllocZ(pParams->cbPacket);
811 if (!pBuf)
812 return RTTestIFailedRc(VERR_NO_MEMORY, "Out of memory");
813
814 /*
815 * Receive first, then Send. The reverse of the client.
816 */
817 NETPERFSTATS RecvStats;
818 int rc = netperfTCPThroughputRecv(pParams, pBuf, &RecvStats);
819 if (RT_SUCCESS(rc))
820 {
821 rc = netperfSendStats(&RecvStats, pParams->hSocket);
822 if (RT_SUCCESS(rc))
823 {
824 NETPERFSTATS SendStats;
825 rc = netperfTCPThroughputSend(pParams, pBuf, &SendStats);
826 if (RT_SUCCESS(rc))
827 {
828 rc = netperfSendStats(&SendStats, pParams->hSocket);
829 netperfPrintThroughputStats(&SendStats, &RecvStats, pParams->cbPacket);
830 }
831 }
832 }
833
834 return rc;
835}
836
837/**
838 * TCP Server: Throughput xmit test (receive from client).
839 *
840 * @returns IPRT status code.
841 * @param pParams The parameters to use for this test.
842 */
843static int netperfTCPServerDoThroughputXmit(NETPERFPARAMS const *pParams)
844{
845 /*
846 * Allocate the buffer.
847 */
848 NETPERFHDR *pBuf = (NETPERFHDR *)RTMemAllocZ(pParams->cbPacket);
849 if (!pBuf)
850 return RTTestIFailedRc(VERR_NO_MEMORY, "Out of memory");
851
852 /*
853 * Receive the transmitted data (reverse of client).
854 */
855 NETPERFSTATS RecvStats;
856 int rc = netperfTCPThroughputRecv(pParams, pBuf, &RecvStats);
857 if (RT_SUCCESS(rc))
858 {
859 rc = netperfSendStats(&RecvStats, pParams->hSocket);
860 if (RT_SUCCESS(rc))
861 netperfPrintThroughputStats(NULL, &RecvStats, pParams->cbPacket);
862 }
863
864 return rc;
865}
866
867/**
868 * TCP Server: Throughput recv test (transmit to client).
869 *
870 * @returns IPRT status code.
871 * @param pParams The parameters to use for this test.
872 */
873static int netperfTCPServerDoThroughputRecv(NETPERFPARAMS const *pParams)
874{
875 /*
876 * Allocate the buffer.
877 */
878 NETPERFHDR *pBuf = (NETPERFHDR *)RTMemAllocZ(pParams->cbPacket);
879 if (!pBuf)
880 return RTTestIFailedRc(VERR_NO_MEMORY, "Out of memory");
881
882 /*
883 * Send data to the client (reverse of client).
884 */
885 NETPERFSTATS SendStats;
886 int rc = netperfTCPThroughputSend(pParams, pBuf, &SendStats);
887 if (RT_SUCCESS(rc))
888 {
889 rc = netperfSendStats(&SendStats, pParams->hSocket);
890 if (RT_SUCCESS(rc))
891 netperfPrintThroughputStats(&SendStats, NULL, pParams->cbPacket);
892 }
893
894 return rc;
895}
896
897/**
898 * TCP Server: Latency test.
899 *
900 * @returns IPRT status code.
901 * @param pParams The parameters to use for this test.
902 */
903static int netperfTCPServerDoLatency(NETPERFPARAMS const *pParams)
904{
905 NETPERFHDR *pBuf = (NETPERFHDR *)RTMemAllocZ(pParams->cbPacket);
906 if (!pBuf)
907 return RTTestIFailedRc(VERR_NO_MEMORY, "Failed to allocated packet buffer of %u bytes.\n", pParams->cbPacket);
908
909 /*
910 * Ping pong with client.
911 */
912 int rc;
913 uint32_t uState = RT_H2LE_U32_C(NETPERFHDR_WARMUP);
914 uint32_t u32Seq = 0;
915 uint64_t cTx = 0;
916 uint64_t cRx = 0;
917 uint64_t u64StartTS = 0;
918 NETPERFSTATS Stats;
919 RT_ZERO(Stats);
920 for (;;)
921 {
922 rc = RTTcpRead(pParams->hSocket, pBuf, pParams->cbPacket, NULL);
923 if (RT_FAILURE(rc))
924 {
925 RTTestIFailed("Failed to read data from client: %Rrc\n", rc);
926 break;
927 }
928
929 /*
930 * Validate the packet
931 */
932 if (RT_UNLIKELY( pBuf->u32Magic != RT_H2LE_U32_C(NETPERFHDR_MAGIC)
933 || pBuf->u32Reserved != 0))
934 {
935 RTTestIFailed("Invalid magic or reserved field value: %#x %#x\n", RT_H2LE_U32(pBuf->u32Magic), RT_H2LE_U32(pBuf->u32Reserved));
936 rc = VERR_INVALID_MAGIC;
937 break;
938 }
939
940 u32Seq += 1;
941 if (RT_UNLIKELY(pBuf->u32Seq != RT_H2LE_U32(u32Seq)))
942 {
943 RTTestIFailed("Out of sequence: got %#x, expected %#x\n", RT_H2LE_U32(pBuf->u32Seq), u32Seq);
944 rc = VERR_WRONG_ORDER;
945 break;
946 }
947
948 /*
949 * Count the packet if the state remains unchanged.
950 */
951 if (RT_LIKELY(pBuf->u32State == uState))
952 cRx++;
953 /*
954 * Validate and act on the state transition.
955 */
956 else if ( uState == RT_H2LE_U32_C(NETPERFHDR_WARMUP)
957 && pBuf->u32State == RT_H2LE_U32_C(NETPERFHDR_TESTING))
958 {
959 cRx = cTx = 0;
960 u64StartTS = RTTimeNanoTS();
961 uState = pBuf->u32State;
962 }
963 else if ( uState == RT_H2LE_U32_C(NETPERFHDR_TESTING)
964 && ( pBuf->u32State == RT_H2LE_U32_C(NETPERFHDR_COOL_DOWN)
965 || pBuf->u32State == RT_H2LE_U32_C(NETPERFHDR_DONE)) )
966 {
967 Stats.cNsElapsed = RTTimeNanoTS() - u64StartTS;
968 Stats.cEchos = cTx;
969 Stats.cTx = cTx;
970 Stats.cRx = cRx;
971 uState = pBuf->u32State;
972 if (uState == RT_H2LE_U32_C(NETPERFHDR_DONE))
973 break;
974 }
975 else if ( uState == RT_H2LE_U32_C(NETPERFHDR_COOL_DOWN)
976 && pBuf->u32State == RT_H2LE_U32_C(NETPERFHDR_DONE))
977 {
978 uState = pBuf->u32State;
979 break;
980 }
981 else
982 {
983 RTTestIFailed("Protocol error: invalid state transition %#x -> %#x\n",
984 RT_LE2H_U32(uState), RT_LE2H_U32(pBuf->u32State));
985 break;
986 }
987
988 /*
989 * Write same data back to client.
990 */
991 rc = RTTcpWrite(pParams->hSocket, pBuf, pParams->cbPacket);
992 if (RT_FAILURE(rc))
993 {
994 RTTestIFailed("Failed to write data to client: %Rrc\n", rc);
995 break;
996 }
997
998 cTx++;
999 }
1000
1001 /*
1002 * Send stats to client and print them.
1003 */
1004 if (uState == RT_H2LE_U32_C(NETPERFHDR_DONE))
1005 netperfSendStats(&Stats, pParams->hSocket);
1006
1007 if ( uState == RT_H2LE_U32_C(NETPERFHDR_DONE)
1008 || uState == RT_H2LE_U32_C(NETPERFHDR_COOL_DOWN))
1009 netperfPrintLatencyStats(&Stats, pParams->cbPacket);
1010
1011 RTMemFree(pBuf);
1012 return rc;
1013}
1014
1015/**
1016 * Parses the parameters the client has sent us.
1017 *
1018 * @returns IPRT status code. Message has been shown on failure.
1019 * @param pParams The parameter structure to store the parameters
1020 * in.
1021 * @param pszParams The parameter string sent by the client.
1022 */
1023static int netperfTCPServerParseParams(NETPERFPARAMS *pParams, char *pszParams)
1024{
1025 /*
1026 * Set defaults for the dynamic settings.
1027 */
1028 pParams->fNoDelay = false;
1029 pParams->enmMode = NETPERFMODE_LATENCY;
1030 pParams->cSecTimeout = NETPERF_DEFAULT_WARMUP;
1031 pParams->cMsCoolDown = NETPERF_DEFAULT_COOL_DOWN;
1032 pParams->cMsWarmup = NETPERF_DEFAULT_WARMUP;
1033 pParams->cbPacket = NETPERF_DEFAULT_PKT_SIZE_LATENCY;
1034
1035 /*
1036 * Parse the client parameters.
1037 */
1038 /* first arg: transport type. [mandatory] */
1039 char *pszCur = strchr(pszParams, ':');
1040 if (!pszCur)
1041 return RTTestIFailedRc(VERR_PARSE_ERROR, "client params: No colon\n");
1042 char *pszNext = strchr(++pszCur, ':');
1043 if (pszNext)
1044 *pszNext++ = '\0';
1045 if (strcmp(pszCur, "TCP"))
1046 return RTTestIFailedRc(VERR_PARSE_ERROR, "client params: Invalid transport type: \"%s\"\n", pszCur);
1047 pszCur = pszNext;
1048
1049 /* second arg: mode. [mandatory] */
1050 if (!pszCur)
1051 return RTTestIFailedRc(VERR_PARSE_ERROR, "client params: Missing test mode\n");
1052 pszNext = strchr(pszCur, ':');
1053 if (pszNext)
1054 *pszNext++ = '\0';
1055 pParams->enmMode = netperfModeFromString(pszCur);
1056 if (pParams->enmMode == NETPERFMODE_INVALID)
1057 return RTTestIFailedRc(VERR_PARSE_ERROR, "client params: Invalid test mode: \"%s\"\n", pszCur);
1058 pszCur = pszNext;
1059
1060 /*
1061 * The remainder are uint32_t or bool.
1062 */
1063 struct
1064 {
1065 bool fBool;
1066 bool fMandatory;
1067 void *pvValue;
1068 uint32_t uMin;
1069 uint32_t uMax;
1070 const char *pszName;
1071 } aElements[] =
1072 {
1073 { false, true, &pParams->cSecTimeout, NETPERF_MIN_TIMEOUT, NETPERF_MAX_TIMEOUT, "timeout" },
1074 { false, true, &pParams->cbPacket, NETPERF_MIN_PKT_SIZE, NETPERF_MAX_PKT_SIZE, "packet size" },
1075 { false, true, &pParams->cMsWarmup, NETPERF_MIN_WARMUP, NETPERF_MAX_WARMUP, "warmup period" },
1076 { false, true, &pParams->cMsCoolDown, NETPERF_MIN_COOL_DOWN, NETPERF_MAX_COOL_DOWN, "cool down period" },
1077 { true, true, &pParams->fNoDelay, false, true, "no delay" },
1078 };
1079
1080 for (unsigned i = 0; i < RT_ELEMENTS(aElements); i++)
1081 {
1082 if (!pszCur)
1083 return aElements[i].fMandatory
1084 ? RTTestIFailedRc(VERR_PARSE_ERROR, "client params: missing %s\n", aElements[i].pszName)
1085 : VINF_SUCCESS;
1086
1087 pszNext = strchr(pszCur, ':');
1088 if (pszNext)
1089 *pszNext++ = '\0';
1090 uint32_t u32;
1091 int rc = RTStrToUInt32Full(pszCur, 10, &u32);
1092 if (rc != VINF_SUCCESS)
1093 return RTTestIFailedRc(VERR_PARSE_ERROR, "client params: bad %s value \"%s\": %Rrc\n",
1094 aElements[i].pszName, pszCur, rc);
1095
1096 if ( u32 < aElements[i].uMin
1097 || u32 > aElements[i].uMax)
1098 return RTTestIFailedRc(VERR_PARSE_ERROR, "client params: %s %u s is out of range (%u..%u)\n",
1099 aElements[i].pszName, u32, aElements[i].uMin, aElements[i].uMax);
1100 if (aElements[i].fBool)
1101 *(bool *)aElements[i].pvValue = u32 ? true : false;
1102 else
1103 *(uint32_t *)aElements[i].pvValue = u32;
1104
1105 pszCur = pszNext;
1106 }
1107
1108 /* Fail if too many elements. */
1109 if (pszCur)
1110 return RTTestIFailedRc(VERR_PARSE_ERROR, "client params: too many elements: \"%s\"\n",
1111 pszCur);
1112 return VINF_SUCCESS;
1113}
1114
1115
1116/**
1117 * TCP server callback that handles one client connection.
1118 *
1119 * @returns IPRT status code. VERR_TCP_SERVER_STOP is special.
1120 * @param hSocket The client socket.
1121 * @param pvUser Our parameters.
1122 */
1123static DECLCALLBACK(int) netperfTCPServerWorker(RTSOCKET hSocket, void *pvUser)
1124{
1125 NETPERFPARAMS *pParams = (NETPERFPARAMS *)pvUser;
1126 AssertReturn(pParams, VERR_INVALID_POINTER);
1127
1128 pParams->hSocket = hSocket;
1129
1130 RTNETADDR Addr;
1131 int rc = RTTcpGetPeerAddress(hSocket, &Addr);
1132 if (RT_SUCCESS(rc))
1133 RTTestIPrintf(RTTESTLVL_ALWAYS, "Client connected from %RTnaddr\n", &Addr);
1134 else
1135 {
1136 RTTestIPrintf(RTTESTLVL_ALWAYS, "Failed to get client details: %Rrc\n", rc);
1137 Addr.enmType = RTNETADDRTYPE_INVALID;
1138 }
1139
1140 /*
1141 * Greet the other dude.
1142 */
1143 rc = RTTcpWrite(hSocket, g_ConnectStart, sizeof(g_ConnectStart) - 1);
1144 if (RT_FAILURE(rc))
1145 return RTTestIFailedRc(rc, "Failed to send connection start Id: %Rrc\n", rc);
1146
1147 /*
1148 * Read connection parameters.
1149 */
1150 char szBuf[256];
1151 rc = RTTcpRead(hSocket, szBuf, NETPERF_LEN_PREFIX, NULL);
1152 if (RT_FAILURE(rc))
1153 return RTTestIFailedRc(rc, "Failed to read connection parameters: %Rrc\n", rc);
1154 szBuf[NETPERF_LEN_PREFIX] = '\0';
1155 uint32_t cchParams;
1156 rc = RTStrToUInt32Full(szBuf, 10, &cchParams);
1157 if (rc != VINF_SUCCESS)
1158 return RTTestIFailedRc(RT_SUCCESS(rc) ? VERR_INTERNAL_ERROR : rc,
1159 "Failed to read connection parameters: %Rrc\n", rc);
1160 if (cchParams >= sizeof(szBuf))
1161 return RTTestIFailedRc(VERR_TOO_MUCH_DATA, "parameter packet is too big (%u bytes)\n", cchParams);
1162 rc = RTTcpRead(hSocket, szBuf, cchParams, NULL);
1163 if (RT_FAILURE(rc))
1164 return RTTestIFailedRc(rc, "Failed to read connection parameters: %Rrc\n", rc);
1165 szBuf[cchParams] = '\0';
1166
1167 if (strncmp(szBuf, g_szStartParams, sizeof(g_szStartParams) - 1))
1168 return RTTestIFailedRc(VERR_NET_PROTOCOL_ERROR, "Invalid connection parameters '%s'\n", szBuf);
1169
1170 /*
1171 * Parse the parameters and signal whether we've got a deal or not.
1172 */
1173 rc = netperfTCPServerParseParams(pParams, szBuf);
1174 if (RT_FAILURE(rc))
1175 {
1176 int rc2 = RTTcpWrite(hSocket, g_szNegative, sizeof(g_szNegative) - 1);
1177 if (RT_FAILURE(rc2))
1178 RTTestIFailed("Failed to send negative ack: %Rrc\n", rc2);
1179 return rc;
1180 }
1181
1182 if (Addr.enmType != RTNETADDRTYPE_INVALID)
1183 RTTestISubF("%RTnaddr - %s, %u s, %u bytes", &Addr,
1184 netperfModeToString(pParams->enmMode), pParams->cSecTimeout, pParams->cbPacket);
1185 else
1186 RTTestISubF("Unknown - %s, %u s, %u bytes",
1187 netperfModeToString(pParams->enmMode), pParams->cSecTimeout, pParams->cbPacket);
1188
1189 rc = RTTcpSetSendCoalescing(hSocket, !pParams->fNoDelay);
1190 if (RT_FAILURE(rc))
1191 return RTTestIFailedRc(rc, "Failed to apply no-delay option (%RTbool): %Rrc\n", pParams->fNoDelay, rc);
1192
1193 rc = RTTcpWrite(hSocket, g_szAck, sizeof(g_szAck) - 1);
1194 if (RT_FAILURE(rc))
1195 return RTTestIFailedRc(rc, "Failed to send start test commend to client: %Rrc\n", rc);
1196
1197 /*
1198 * Take action according to our mode.
1199 */
1200 switch (pParams->enmMode)
1201 {
1202 case NETPERFMODE_LATENCY:
1203 rc = netperfTCPServerDoLatency(pParams);
1204 break;
1205
1206 case NETPERFMODE_THROUGHPUT:
1207 rc = netperfTCPServerDoThroughput(pParams);
1208 break;
1209
1210 case NETPERFMODE_THROUGHPUT_XMIT:
1211 rc = netperfTCPServerDoThroughputXmit(pParams);
1212 break;
1213
1214 case NETPERFMODE_THROUGHPUT_RECV:
1215 rc = netperfTCPServerDoThroughputRecv(pParams);
1216 break;
1217
1218 case NETPERFMODE_INVALID:
1219 rc = VERR_INTERNAL_ERROR;
1220 break;
1221
1222 /* no default! */
1223 }
1224 if (rc == VERR_NO_MEMORY)
1225 return VERR_TCP_SERVER_STOP;
1226
1227 /*
1228 * Wait for other clients or quit.
1229 */
1230 if (pParams->fSingleClient)
1231 return VERR_TCP_SERVER_STOP;
1232 return VINF_SUCCESS;
1233}
1234
1235
1236/**
1237 * TCP server.
1238 *
1239 * @returns IPRT status code.
1240 * @param pParams The TCP parameter block.
1241 */
1242static int netperfTCPServer(NETPERFPARAMS *pParams)
1243{
1244 /*
1245 * Spawn the TCP server thread & listen.
1246 */
1247 PRTTCPSERVER pServer;
1248 int rc = RTTcpServerCreateEx(NULL, pParams->uPort, &pServer);
1249 if (RT_SUCCESS(rc))
1250 {
1251 RTPrintf("Server listening on TCP port %d\n", pParams->uPort);
1252 rc = RTTcpServerListen(pServer, netperfTCPServerWorker, pParams);
1253 RTTcpServerDestroy(pServer);
1254 }
1255 else
1256 RTPrintf("Failed to create TCP server thread: %Rrc\n", rc);
1257
1258 return rc;
1259}
1260
1261/**
1262 * The server part.
1263 *
1264 * @returns Exit code.
1265 * @param enmProto The protocol.
1266 * @param pParams The parameter block.
1267 */
1268static RTEXITCODE netperfServer(NETPERFPROTO enmProto, NETPERFPARAMS *pParams)
1269{
1270
1271 switch (enmProto)
1272 {
1273 case NETPERFPROTO_TCP:
1274 {
1275 int rc = netperfTCPServer(pParams);
1276 return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1277 }
1278
1279 default:
1280 RTTestIFailed("Protocol not supported.\n");
1281 return RTEXITCODE_FAILURE;
1282 }
1283}
1284
1285
1286
1287
1288
1289/**
1290 * TCP client: Do the throughput test.
1291 *
1292 * @returns IPRT status code
1293 * @param pParams The parameters.
1294 */
1295static int netperfTCPClientDoThroughput(NETPERFPARAMS *pParams)
1296{
1297 /*
1298 * Allocate the buffer.
1299 */
1300 NETPERFHDR *pBuf = (NETPERFHDR *)RTMemAllocZ(pParams->cbPacket);
1301 if (!pBuf)
1302 return RTTestIFailedRc(VERR_NO_MEMORY, "Out of memory");
1303
1304 /*
1305 * Send first, then Receive.
1306 */
1307 NETPERFSTATS SendStats;
1308 int rc = netperfTCPThroughputSend(pParams, pBuf, &SendStats);
1309 if (RT_SUCCESS(rc))
1310 {
1311 NETPERFSTATS SrvSendStats;
1312 rc = netperfRecvStats(&SrvSendStats, pParams->hSocket);
1313 if (RT_SUCCESS(rc))
1314 {
1315 NETPERFSTATS RecvStats;
1316 rc = netperfTCPThroughputRecv(pParams, pBuf, &RecvStats);
1317 if (RT_SUCCESS(rc))
1318 {
1319 NETPERFSTATS SrvRecvStats;
1320 rc = netperfRecvStats(&SrvRecvStats, pParams->hSocket);
1321 if (RT_SUCCESS(rc))
1322 {
1323 if (pParams->fServerStats)
1324 netperfPrintThroughputStats(&SrvSendStats, &SrvRecvStats, pParams->cbPacket);
1325 else
1326 netperfPrintThroughputStats(&SendStats, &RecvStats, pParams->cbPacket);
1327 }
1328 }
1329 }
1330 }
1331
1332 RTTestISubDone();
1333 return rc;
1334}
1335
1336/**
1337 * TCP client: Do the throughput xmit test.
1338 *
1339 * @returns IPRT status code
1340 * @param pParams The parameters.
1341 */
1342static int netperfTCPClientDoThroughputXmit(NETPERFPARAMS *pParams)
1343{
1344 /*
1345 * Allocate the buffer.
1346 */
1347 NETPERFHDR *pBuf = (NETPERFHDR *)RTMemAllocZ(pParams->cbPacket);
1348 if (!pBuf)
1349 return RTTestIFailedRc(VERR_NO_MEMORY, "Out of memory");
1350
1351 /*
1352 * Do the job.
1353 */
1354 NETPERFSTATS SendStats;
1355 int rc = netperfTCPThroughputSend(pParams, pBuf, &SendStats);
1356 if (RT_SUCCESS(rc))
1357 {
1358 NETPERFSTATS SrvSendStats;
1359 rc = netperfRecvStats(&SrvSendStats, pParams->hSocket);
1360 if (RT_SUCCESS(rc))
1361 {
1362 if (pParams->fServerStats)
1363 netperfPrintThroughputStats(&SrvSendStats, NULL, pParams->cbPacket);
1364 else
1365 netperfPrintThroughputStats(&SendStats, NULL, pParams->cbPacket);
1366 }
1367 }
1368
1369 RTTestISubDone();
1370 return rc;
1371}
1372
1373/**
1374 * TCP client: Do the throughput recv test.
1375 *
1376 * @returns IPRT status code
1377 * @param pParams The parameters.
1378 */
1379static int netperfTCPClientDoThroughputRecv(NETPERFPARAMS *pParams)
1380{
1381 /*
1382 * Allocate the buffer.
1383 */
1384 NETPERFHDR *pBuf = (NETPERFHDR *)RTMemAllocZ(pParams->cbPacket);
1385 if (!pBuf)
1386 return RTTestIFailedRc(VERR_NO_MEMORY, "Out of memory");
1387
1388 /*
1389 * Do the job.
1390 */
1391 NETPERFSTATS RecvStats;
1392 int rc = netperfTCPThroughputRecv(pParams, pBuf, &RecvStats);
1393 if (RT_SUCCESS(rc))
1394 {
1395 NETPERFSTATS SrvRecvStats;
1396 rc = netperfRecvStats(&SrvRecvStats, pParams->hSocket);
1397 if (RT_SUCCESS(rc))
1398 {
1399 if (pParams->fServerStats)
1400 netperfPrintThroughputStats(NULL, &SrvRecvStats, pParams->cbPacket);
1401 else
1402 netperfPrintThroughputStats(NULL, &RecvStats, pParams->cbPacket);
1403 }
1404 }
1405
1406 RTTestISubDone();
1407 return rc;
1408}
1409
1410/**
1411 * TCP client: Do the latency test.
1412 *
1413 * @returns IPRT status code
1414 * @param pParams The parameters.
1415 */
1416static int netperfTCPClientDoLatency(NETPERFPARAMS *pParams)
1417{
1418 /*
1419 * Generate a selection of packages before we start, after all we're not
1420 * benchmarking the random number generator, are we. :-)
1421 */
1422 void *pvReadBuf = RTMemAllocZ(pParams->cbPacket);
1423 if (!pvReadBuf)
1424 return RTTestIFailedRc(VERR_NO_MEMORY, "Out of memory");
1425
1426 size_t i;
1427 NETPERFHDR *apPackets[256];
1428 for (i = 0; i < RT_ELEMENTS(apPackets); i++)
1429 {
1430 apPackets[i] = (NETPERFHDR *)RTMemAllocZ(pParams->cbPacket);
1431 if (!apPackets[i])
1432 {
1433 while (i-- > 0)
1434 RTMemFree(apPackets[i]);
1435 RTMemFree(pvReadBuf);
1436 return RTTestIFailedRc(VERR_NO_MEMORY, "Out of memory");
1437 }
1438 RTRandBytes(apPackets[i], pParams->cbPacket);
1439 apPackets[i]->u32Magic = RT_H2LE_U32_C(NETPERFHDR_MAGIC);
1440 apPackets[i]->u32State = 0;
1441 apPackets[i]->u32Seq = 0;
1442 apPackets[i]->u32Reserved = 0;
1443 }
1444
1445 /*
1446 * Create & start a timer to eventually disconnect.
1447 */
1448 bool volatile fStop = false;
1449 RTTIMERLR hTimer;
1450 int rc = RTTimerLRCreateEx(&hTimer, 0 /* nsec */, RTTIMER_FLAGS_CPU_ANY, netperfStopTimerCallback, (void *)&fStop);
1451 if (RT_SUCCESS(rc))
1452 {
1453 uint32_t u32Seq = 0;
1454 NETPERFSTATS Stats;
1455 RT_ZERO(Stats);
1456
1457 /*
1458 * Warm up.
1459 */
1460 if (g_uVerbosity > 0)
1461 RTPrintf("Warmup...\n");
1462 rc = RTTimerLRStart(hTimer, pParams->cMsWarmup * UINT64_C(1000000) /* nsec */);
1463 if (RT_SUCCESS(rc))
1464 {
1465 while (!fStop)
1466 {
1467 NETPERFHDR *pPacket = apPackets[u32Seq % RT_ELEMENTS(apPackets)];
1468 u32Seq++;
1469 pPacket->u32Seq = RT_H2LE_U32(u32Seq);
1470 pPacket->u32State = RT_H2LE_U32_C(NETPERFHDR_WARMUP);
1471 rc = RTTcpWrite(pParams->hSocket, pPacket, pParams->cbPacket);
1472 if (RT_FAILURE(rc))
1473 {
1474 RTTestIFailed("RTTcpWrite/warmup: %Rrc\n", rc);
1475 break;
1476 }
1477 rc = RTTcpRead(pParams->hSocket, pvReadBuf, pParams->cbPacket, NULL);
1478 if (RT_FAILURE(rc))
1479 {
1480 RTTestIFailed("RTTcpRead/warmup: %Rrc\n", rc);
1481 break;
1482 }
1483 }
1484 }
1485 else
1486 RTTestIFailed("RTTimerLRStart/warmup: %Rrc\n", rc);
1487
1488 /*
1489 * The real thing.
1490 */
1491 if (RT_SUCCESS(rc))
1492 {
1493 if (g_uVerbosity > 0)
1494 RTPrintf("The real thing...\n");
1495 fStop = false;
1496 rc = RTTimerLRStart(hTimer, pParams->cSecTimeout * UINT64_C(1000000000) /* nsec */);
1497 if (RT_SUCCESS(rc))
1498 {
1499 uint64_t u64StartTS = RTTimeNanoTS();
1500 while (!fStop)
1501 {
1502 NETPERFHDR *pPacket = apPackets[u32Seq % RT_ELEMENTS(apPackets)];
1503 u32Seq++;
1504 pPacket->u32Seq = RT_H2LE_U32(u32Seq);
1505 pPacket->u32State = RT_H2LE_U32_C(NETPERFHDR_TESTING);
1506 rc = RTTcpWrite(pParams->hSocket, pPacket, pParams->cbPacket);
1507 if (RT_FAILURE(rc))
1508 {
1509 RTTestIFailed("RTTcpWrite/testing: %Rrc\n", rc);
1510 break;
1511 }
1512 Stats.cTx++;
1513
1514 rc = RTTcpRead(pParams->hSocket, pvReadBuf, pParams->cbPacket, NULL);
1515 if (RT_FAILURE(rc))
1516 {
1517 RTTestIFailed("RTTcpRead/testing: %Rrc\n", rc);
1518 break;
1519 }
1520 Stats.cRx++;
1521
1522 if (!memcmp(pvReadBuf, pPacket, pParams->cbPacket))
1523 Stats.cEchos++;
1524 else
1525 Stats.cErrors++;
1526 }
1527 Stats.cNsElapsed = RTTimeNanoTS() - u64StartTS;
1528 }
1529 else
1530 RTTestIFailed("RTTimerLRStart/testing: %Rrc\n", rc);
1531 }
1532
1533 /*
1534 * Cool down.
1535 */
1536 if (RT_SUCCESS(rc))
1537 {
1538 if (g_uVerbosity > 0)
1539 RTPrintf("Cool down...\n");
1540 fStop = false;
1541 rc = RTTimerLRStart(hTimer, pParams->cMsCoolDown * UINT64_C(1000000) /* nsec */);
1542 if (RT_SUCCESS(rc))
1543 {
1544 while (!fStop)
1545 {
1546 NETPERFHDR *pPacket = apPackets[u32Seq % RT_ELEMENTS(apPackets)];
1547 u32Seq++;
1548 pPacket->u32Seq = RT_H2LE_U32(u32Seq);
1549 pPacket->u32State = RT_H2LE_U32_C(NETPERFHDR_COOL_DOWN);
1550 rc = RTTcpWrite(pParams->hSocket, pPacket, pParams->cbPacket);
1551 if (RT_FAILURE(rc))
1552 {
1553 RTTestIFailed("RTTcpWrite/warmup: %Rrc\n", rc);
1554 break;
1555 }
1556 rc = RTTcpRead(pParams->hSocket, pvReadBuf, pParams->cbPacket, NULL);
1557 if (RT_FAILURE(rc))
1558 {
1559 RTTestIFailed("RTTcpRead/warmup: %Rrc\n", rc);
1560 break;
1561 }
1562 }
1563 }
1564 else
1565 RTTestIFailed("RTTimerLRStart/testing: %Rrc\n", rc);
1566 }
1567
1568 /*
1569 * Send DONE packet.
1570 */
1571 if (g_uVerbosity > 0)
1572 RTPrintf("Done\n");
1573 if (RT_SUCCESS(rc))
1574 {
1575 u32Seq++;
1576 NETPERFHDR *pPacket = apPackets[u32Seq % RT_ELEMENTS(apPackets)];
1577 pPacket->u32Seq = RT_H2LE_U32(u32Seq);
1578 pPacket->u32State = RT_H2LE_U32_C(NETPERFHDR_DONE);
1579 rc = RTTcpWrite(pParams->hSocket, pPacket, pParams->cbPacket);
1580 if (RT_FAILURE(rc))
1581 RTTestIFailed("RTTcpWrite/done: %Rrc\n", rc);
1582 }
1583
1584
1585 /*
1586 * Get and print stats.
1587 */
1588 NETPERFSTATS SrvStats;
1589 if (RT_SUCCESS(rc))
1590 {
1591 rc = netperfRecvStats(&SrvStats, pParams->hSocket);
1592 if (RT_SUCCESS(rc) && pParams->fServerStats)
1593 netperfPrintLatencyStats(&SrvStats, pParams->cbPacket);
1594 else if (!pParams->fServerStats)
1595 netperfPrintLatencyStats(&Stats, pParams->cbPacket);
1596 }
1597
1598 /* clean up*/
1599 RTTimerLRDestroy(hTimer);
1600 }
1601 else
1602 RTTestIFailed("Failed to create timer object: %Rrc\n", rc);
1603 for (i = 0; i < RT_ELEMENTS(apPackets); i++)
1604 RTMemFree(apPackets[i]);
1605
1606 RTMemFree(pvReadBuf);
1607
1608 return rc;
1609}
1610
1611/**
1612 * TCP client test driver.
1613 *
1614 * @returns IPRT status code
1615 * @param pszServer The server name.
1616 * @param pParams The parameter structure.
1617 */
1618static int netperfTCPClient(const char *pszServer, NETPERFPARAMS *pParams)
1619{
1620 AssertReturn(pParams, VERR_INVALID_POINTER);
1621 RTTestISubF("TCP - %u s, %u bytes%s", pParams->cSecTimeout,
1622 pParams->cbPacket, pParams->fNoDelay ? ", no delay" : "");
1623
1624 RTSOCKET hSocket = NIL_RTSOCKET;
1625 int rc = RTTcpClientConnect(pszServer, pParams->uPort, &hSocket);
1626 if (RT_FAILURE(rc))
1627 return RTTestIFailedRc(rc, "Failed to connect to %s on port %u: %Rrc\n", pszServer, pParams->uPort, rc);
1628 pParams->hSocket = hSocket;
1629
1630 /*
1631 * Disable send coalescing (no-delay).
1632 */
1633 if (pParams->fNoDelay)
1634 {
1635 rc = RTTcpSetSendCoalescing(hSocket, false /*fEnable*/);
1636 if (RT_FAILURE(rc))
1637 return RTTestIFailedRc(rc, "Failed to set no-delay option: %Rrc\n", rc);
1638 }
1639
1640 /*
1641 * Verify the super secret Start Connect Id to start the connection.
1642 */
1643 char szBuf[256 + NETPERF_LEN_PREFIX];
1644 RT_ZERO(szBuf);
1645 rc = RTTcpRead(hSocket, szBuf, sizeof(g_ConnectStart) - 1, NULL);
1646 if (RT_FAILURE(rc))
1647 return RTTestIFailedRc(rc, "Failed to read connection initializer: %Rrc\n", rc);
1648
1649 if (strcmp(szBuf, g_ConnectStart))
1650 return RTTestIFailedRc(VERR_INVALID_MAGIC, "Invalid connection initializer '%s'\n", szBuf);
1651
1652 /*
1653 * Send all the dynamic parameters to the server.
1654 * (If the server is newer than the client, it will select default for any
1655 * missing parameters.)
1656 */
1657 size_t cchParams = RTStrPrintf(&szBuf[NETPERF_LEN_PREFIX], sizeof(szBuf) - NETPERF_LEN_PREFIX,
1658 "%s:%s:%s:%u:%u:%u:%u:%u",
1659 g_szStartParams,
1660 "TCP",
1661 netperfModeToString(pParams->enmMode),
1662 pParams->cSecTimeout,
1663 pParams->cbPacket,
1664 pParams->cMsWarmup,
1665 pParams->cMsCoolDown,
1666 pParams->fNoDelay);
1667 RTStrPrintf(szBuf, NETPERF_LEN_PREFIX + 1, "%0*u", NETPERF_LEN_PREFIX, cchParams);
1668 szBuf[NETPERF_LEN_PREFIX] = g_szStartParams[0];
1669 Assert(strlen(szBuf) == NETPERF_LEN_PREFIX + cchParams);
1670 rc = RTTcpWrite(hSocket, szBuf, NETPERF_LEN_PREFIX + cchParams);
1671 if (RT_FAILURE(rc))
1672 return RTTestIFailedRc(rc, "Failed to send connection parameters: %Rrc\n", rc);
1673
1674 /*
1675 * Wait for acknowledgment.
1676 */
1677 rc = RTTcpRead(hSocket, szBuf, sizeof(g_szAck) - 1, NULL);
1678 if (RT_FAILURE(rc))
1679 return RTTestIFailedRc(rc, "Failed to send parameters: %Rrc\n", rc);
1680 szBuf[sizeof(g_szAck) - 1] = '\0';
1681
1682 if (!strcmp(szBuf, g_szNegative))
1683 return RTTestIFailedRc(VERR_NET_PROTOCOL_ERROR, "Server failed to accept packet size of %u bytes.\n", pParams->cbPacket);
1684 if (strcmp(szBuf, g_szAck))
1685 return RTTestIFailedRc(VERR_NET_PROTOCOL_ERROR, "Invalid response from server '%s'\n", szBuf);
1686
1687 /*
1688 * Take action according to our mode.
1689 */
1690 switch (pParams->enmMode)
1691 {
1692 case NETPERFMODE_LATENCY:
1693 RTTestIPrintf(RTTESTLVL_ALWAYS, "Connected to %s port %u, running the latency test for %u seconds.\n",
1694 pszServer, pParams->uPort, pParams->cSecTimeout);
1695 rc = netperfTCPClientDoLatency(pParams);
1696 break;
1697
1698 case NETPERFMODE_THROUGHPUT:
1699 RTTestIPrintf(RTTESTLVL_ALWAYS, "Connected to %s port %u, running the throughput test for %u seconds in each direction.\n",
1700 pszServer, pParams->uPort, pParams->cSecTimeout);
1701 rc = netperfTCPClientDoThroughput(pParams);
1702 break;
1703
1704 case NETPERFMODE_THROUGHPUT_XMIT:
1705 RTTestIPrintf(RTTESTLVL_ALWAYS, "Connected to %s port %u, running the throughput-xmit test for %u seconds.\n",
1706 pszServer, pParams->uPort, pParams->cSecTimeout);
1707 rc = netperfTCPClientDoThroughputXmit(pParams);
1708 break;
1709
1710 case NETPERFMODE_THROUGHPUT_RECV:
1711 RTTestIPrintf(RTTESTLVL_ALWAYS, "Connected to %s port %u, running the throughput-recv test for %u seconds.\n",
1712 pszServer, pParams->uPort, pParams->cSecTimeout);
1713 rc = netperfTCPClientDoThroughputRecv(pParams);
1714 break;
1715
1716 case NETPERFMODE_INVALID:
1717 rc = VERR_INTERNAL_ERROR;
1718 break;
1719
1720 /* no default! */
1721 }
1722 return rc;
1723}
1724
1725/**
1726 * The client part.
1727 *
1728 * @returns Exit code.
1729 * @param enmProto The protocol.
1730 * @param pszServer The server name.
1731 * @param pvUser The parameter block as opaque user data.
1732 */
1733static RTEXITCODE netperfClient(NETPERFPROTO enmProto, const char *pszServer, void *pvUser)
1734{
1735 switch (enmProto)
1736 {
1737 case NETPERFPROTO_TCP:
1738 {
1739 NETPERFPARAMS *pParams = (NETPERFPARAMS *)pvUser;
1740 int rc = netperfTCPClient(pszServer, pParams);
1741 if (pParams->hSocket != NIL_RTSOCKET)
1742 {
1743 RTTcpClientClose(pParams->hSocket);
1744 pParams->hSocket = NIL_RTSOCKET;
1745 }
1746 return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1747 }
1748
1749 default:
1750 RTTestIFailed("Protocol not supported.\n");
1751 return RTEXITCODE_FAILURE;
1752 }
1753}
1754
1755
1756int main(int argc, char *argv[])
1757{
1758 /*
1759 * Init IPRT and globals.
1760 */
1761 int rc = RTTestInitAndCreate("NetPerf", &g_hTest);
1762 if (rc)
1763 return rc;
1764
1765 /*
1766 * Special case.
1767 */
1768 if (argc < 2)
1769 {
1770 RTTestFailed(g_hTest, "No arguments given.");
1771 return RTTestSummaryAndDestroy(g_hTest);
1772 }
1773
1774 /*
1775 * Default values.
1776 */
1777 NETPERFPROTO enmProtocol = NETPERFPROTO_TCP;
1778 bool fServer = true;
1779 bool fDaemonize = false;
1780 bool fDaemonized = false;
1781 bool fPacketSizeSet = false;
1782 const char *pszServerAddress= NULL;
1783
1784 NETPERFPARAMS Params;
1785 Params.uPort = NETPERF_DEFAULT_PORT;
1786 Params.fServerStats = false;
1787 Params.fSingleClient = false;
1788
1789 Params.fNoDelay = false;
1790 Params.fCheckData = false;
1791 Params.enmMode = NETPERFMODE_LATENCY;
1792 Params.cSecTimeout = NETPERF_DEFAULT_TIMEOUT;
1793 Params.cMsWarmup = NETPERF_DEFAULT_WARMUP;
1794 Params.cMsCoolDown = NETPERF_DEFAULT_COOL_DOWN;
1795 Params.cbPacket = NETPERF_DEFAULT_PKT_SIZE_LATENCY;
1796
1797 Params.hSocket = NIL_RTSOCKET;
1798
1799 RTGETOPTUNION ValueUnion;
1800 RTGETOPTSTATE GetState;
1801 RTGetOptInit(&GetState, argc, argv, g_aCmdOptions, RT_ELEMENTS(g_aCmdOptions), 1, 0 /* fFlags */);
1802 while ((rc = RTGetOpt(&GetState, &ValueUnion)))
1803 {
1804 switch (rc)
1805 {
1806 case 's':
1807 fServer = true;
1808 break;
1809
1810 case 'c':
1811 fServer = false;
1812 pszServerAddress = ValueUnion.psz;
1813 break;
1814
1815 case 'd':
1816 fDaemonize = true;
1817 break;
1818
1819 case 'D':
1820 fDaemonized = true;
1821 break;
1822
1823 case 'i':
1824 Params.cSecTimeout = ValueUnion.u32;
1825 if ( Params.cSecTimeout < NETPERF_MIN_TIMEOUT
1826 || Params.cSecTimeout > NETPERF_MAX_TIMEOUT)
1827 {
1828 RTTestFailed(g_hTest, "Invalid interval %u s, valid range: %u-%u\n",
1829 Params.cbPacket, NETPERF_MIN_TIMEOUT, NETPERF_MAX_TIMEOUT);
1830 return RTTestSummaryAndDestroy(g_hTest);
1831 }
1832 break;
1833
1834 case 'l':
1835 Params.cbPacket = ValueUnion.u32;
1836 if ( Params.cbPacket < NETPERF_MIN_PKT_SIZE
1837 || Params.cbPacket > NETPERF_MAX_PKT_SIZE)
1838 {
1839 RTTestFailed(g_hTest, "Invalid packet size %u bytes, valid range: %u-%u\n",
1840 Params.cbPacket, NETPERF_MIN_PKT_SIZE, NETPERF_MAX_PKT_SIZE);
1841 return RTTestSummaryAndDestroy(g_hTest);
1842 }
1843 fPacketSizeSet = true;
1844 break;
1845
1846 case 'm':
1847 Params.enmMode = netperfModeFromString(ValueUnion.psz);
1848 if (Params.enmMode == NETPERFMODE_INVALID)
1849 {
1850 RTTestFailed(g_hTest, "Invalid test mode: \"%s\"\n", ValueUnion.psz);
1851 return RTTestSummaryAndDestroy(g_hTest);
1852 }
1853 if (!fPacketSizeSet)
1854 switch (Params.enmMode)
1855 {
1856 case NETPERFMODE_LATENCY:
1857 Params.cbPacket = NETPERF_DEFAULT_PKT_SIZE_LATENCY;
1858 break;
1859 case NETPERFMODE_THROUGHPUT:
1860 case NETPERFMODE_THROUGHPUT_XMIT:
1861 case NETPERFMODE_THROUGHPUT_RECV:
1862 Params.cbPacket = NETPERF_DEFAULT_PKT_SIZE_THROUGHPUT;
1863 break;
1864 case NETPERFMODE_INVALID:
1865 break;
1866 /* no default! */
1867 }
1868 break;
1869
1870 case 'p':
1871 Params.uPort = ValueUnion.u32;
1872 break;
1873
1874 case 'N':
1875 Params.fNoDelay = true;
1876 break;
1877
1878 case 'S':
1879 Params.fServerStats = true;
1880 break;
1881
1882 case '1':
1883 Params.fSingleClient = true;
1884 break;
1885
1886 case 'v':
1887 g_uVerbosity++;
1888 break;
1889
1890 case 'h':
1891 Usage(g_pStdOut);
1892 return RTEXITCODE_SUCCESS;
1893
1894 case 'V':
1895 RTPrintf("$Revision: 70404 $\n");
1896 return RTEXITCODE_SUCCESS;
1897
1898 case 'w':
1899 Params.cMsWarmup = ValueUnion.u32;
1900 if ( Params.cMsWarmup < NETPERF_MIN_WARMUP
1901 || Params.cMsWarmup > NETPERF_MAX_WARMUP)
1902 {
1903 RTTestFailed(g_hTest, "invalid warmup time %u ms, valid range: %u-%u\n",
1904 Params.cMsWarmup, NETPERF_MIN_WARMUP, NETPERF_MAX_WARMUP);
1905 return RTTestSummaryAndDestroy(g_hTest);
1906 }
1907 break;
1908
1909 case 'W':
1910 Params.cMsCoolDown = ValueUnion.u32;
1911 if ( Params.cMsCoolDown < NETPERF_MIN_COOL_DOWN
1912 || Params.cMsCoolDown > NETPERF_MAX_COOL_DOWN)
1913 {
1914 RTTestFailed(g_hTest, "invalid cool down time %u ms, valid range: %u-%u\n",
1915 Params.cMsCoolDown, NETPERF_MIN_COOL_DOWN, NETPERF_MAX_COOL_DOWN);
1916 return RTTestSummaryAndDestroy(g_hTest);
1917 }
1918 break;
1919
1920 case 'C':
1921 Params.fCheckData = true;
1922 break;
1923
1924 default:
1925 return RTGetOptPrintError(rc, &ValueUnion);
1926 }
1927 }
1928
1929 /*
1930 * Handle the server process daemoniziation.
1931 */
1932 if (fDaemonize && !fDaemonized && fServer)
1933 {
1934 rc = RTProcDaemonize(argv, "--daemonized");
1935 if (RT_FAILURE(rc))
1936 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTProcDaemonize failed: %Rrc\n", rc);
1937 return RTEXITCODE_SUCCESS;
1938 }
1939
1940 /*
1941 * Get down to business.
1942 */
1943 RTTestBanner(g_hTest);
1944 if (fServer)
1945 rc = netperfServer(enmProtocol, &Params);
1946 else if (pszServerAddress)
1947 rc = netperfClient(enmProtocol, pszServerAddress, &Params);
1948 else
1949 RTTestFailed(g_hTest, "missing server address to connect to\n");
1950
1951 RTEXITCODE rc2 = RTTestSummaryAndDestroy(g_hTest);
1952 return rc2 != RTEXITCODE_FAILURE ? (RTEXITCODE)rc2 : rc;
1953}
1954
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette