VirtualBox

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

Last change on this file since 84057 was 82968, checked in by vboxsync, 5 years ago

Copyright year updates by scm.

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