replayGS_example.c

Reference Documentation

product_line_custom
Napatech SmartNIC
category
Reference Information

Go to the documentation of this file.

1/* 2 * %NT_SOFTWARE_LICENSE% 3 */ 4 5/** 6 * @example net/replayGS/replayGS_example.c 7 * @section replayGS_example_description Description 8 * 9 * This source file is an example of how to replay a capture file onto 10 * a port using NTAPI and the global synchronous transmit feature. 11 * 12 * With this example is it possible make a synchronous transmit between 13 * several accelerators placed in either same server or different serves. In order 14 * for the synchronous transmit to work all adapters must be synchronized using 15 * timesync. 16 * 17 * The following NTAPI functions are used: 18 * - @ref NT_Init() 19 * - @ref NT_InfoOpen() 20 * - @ref NT_InfoRead() 21 * - @ref NT_InfoClose() 22 * - @ref NT_ConfigOpen() 23 * - @ref NT_ConfigRead() 24 * - @ref NT_ConfigWrite() 25 * - @ref NT_ConfigClose() 26 * - @ref NT_NetFileOpen() 27 * - @ref NT_NetTxOpen() 28 * - @ref NT_NTPL() 29 * - @ref NT_NetFileGet() 30 * - @ref NT_NetTxGet() 31 * - @ref NT_NET_GET_SEGMENT_PTR() 32 * - @ref NT_NET_GET_SEGMENT_LENGTH() 33 * - @ref NT_NetTxRelease() 34 * - @ref NT_NetFileRelease() 35 * - @ref NT_NetFileClose() 36 * - @ref NT_NetTxClose() 37 * - @ref NT_ExplainError() 38 * 39 * @section replayGS_example_prerequisites Prerequisites 40 * - A capture file, that has been captured with the @ref 41 * net/capture/capture_example.c "net/capture/capture_example.c" example. 42 * - All adapters must support the global sync feature. 43 * - TxTiming must be set to ABSOLUTE in ntservice.ini 44 * - Timestamp format must be NATIVE_UNIX 45 * - ntservice.ini must have at least one HostBuffersTx 46 * defined. Below is an example of a minimum ini-file. It will 47 * create a 32MB TX hostbuffer from NUMA node 0. 48 * @code 49 * [System] 50 * TimestampFormat = NATIVE_UNIX 51 * 52 * [Adapter0] 53 * AdapterType = NT20E2 54 * BusId = 00:0a:00.00 55 * HostBuffersTx = [1,32,0] 56 * TxTiming = ABSOLUTE 57 * @endcode 58 * 59 * @section replayGS_example_flow Program flow 60 * @{ 61 * The following is required to perform synchronized replay of a captured file: 62 * - \#include/nt.h - Applications/Tools only need to include @ref 63 * nt.h to obtain prototypes, macros etc. from NTAPI. 64 * - @ref NT_Init(@ref NTAPI_VERSION) - Initialize the NTAPI 65 * library. @ref NTAPI_VERSION is a define that describes the version 66 * of the API described in the header files included by @ref 67 * nt.h. NT_Init() will ask the NTAPI library to convert return data 68 * to the @ref NTAPI_VERSION if possible. This will ensure that 69 * applications can run on NTAPI libraries of newer versions. 70 * - @ref NT_InfoOpen() - Open the information stream. 71 * - @ref NT_InfoRead() - Read @ref NtInfoAdapter_v6_s "the adapter information" to check whether or not 72 * we are using absolut TX timing. The @ref NtTxTimingMethod_e "txTiming" 73 * tells whether relative or absolut TX timing is enabled. 74 * - @ref NT_InfoClose() - Close the information stream. 75 * - @ref NT_ConfigOpen() - Open the config stream. 76 * - @ref NT_ConfigRead() - Read the adapter timestamp from the adapter. Needed 77 * in order to synchronize the transmit. 78 * - @ref NT_ConfigWrite() - Write the synchronize offset to the adapter. 79 * - @ref NT_ConfigClose() - Close the config stream. 80 * - @ref NT_NetFileOpen() - Open the captured file and assign it to a stream. 81 * - @ref NT_NetTxOpen() - Open a hostbuffer than can transmit packets to port 0. 82 * - @ref NT_NetFileGet() - Get a segment from the file. This call will 83 * return NT_SUCCESS upon return of a segment and 84 * @ref NT_STATUS_END_OF_FILE when there is no more segments 85 * avaialble. The latter will cause the example to exit. 86 * - @ref NT_NetTxGet() - Get an empty tx buffer. This will get a segment of the 87 * requested length from the hostbuffer that will be sent 88 * onto port 0 when the buffer is released. 89 * - @ref NT_NET_GET_SEGMENT_PTR() and @ref NT_NET_GET_SEGMENT_LENGTH() are used 90 * to tell where the segment data is located and how much there is. 91 * @ref _nt_net_build_pkt_netbuf() and @ref _nt_net_get_next_packet() are used to traverse 92 * packets inside a segment. This is usefull if ie. the timing of the packet 93 * transmit rate needs to be changed. 94 * - @ref NT_NetTxRelease() - Release the segment buffer. Once a tx 95 * buffer is released it will be transmitted according to the 96 * timestamps of the packets in the segment. 97 * - @ref NT_NetFileRelease() - Release the segment from the file stream. 98 * - @ref NT_NetFileClose() - Close the file stream when no more segments can be found. 99 * - @ref NT_NetTxClose() - Close the TX stream. 100 * 101 *<hr> 102 * @section replayGS_example_code Code 103 * @} 104 */ 105 106#if defined(__linux__) || defined(__FreeBSD__) 107 #define _GNU_SOURCE 108 #include <string.h> // memcpy 109 #include <stdarg.h> 110 #include <stdlib.h> 111 #include <signal.h> 112 #include <unistd.h> 113 #include <ctype.h> 114#elif defined(WIN32) || defined (WIN64) 115 #include <winsock2.h> 116 #include "strptime.h" 117 #define timegm _mkgmtime 118#endif 119#include <argparse.h> 120#include <time.h> 121#include <nt.h> 122 123 124#if defined(WIN32) || defined (WIN64) 125 #define strtoull _strtoui64 126#endif 127 128static const char *usageText[] = { 129 "The replay example can replay a data file stored with the \"capture\" example.\n" 130 "The packets will be transmitted according to the TxPort field in the\n" 131 "packet descriptors. The TxPort field is relative to port0 on the adapter\n" 132 "provided the packets and therefore it is needed to select a port0 offset\n" 133 "via the option \"-p\".\n" 134 "The replay rate will be identical to the rate at which the packets were received.\n" 135 "\n" 136 "Syntax:\n" 137 "\n" 138 "replayGS -f <file> [-p <port offset>] [-g <timestamp>] [-s <starttime>] [-t] [--help] \n" 139 "\nCommands:\n", 140 NULL}; 141 142 static char *opt_file = NULL; 143 static int opt_portOffset = -1; 144 static char *opt_start = NULL; 145 static int opt_time = 0; 146 147static int appRunning=1; 148static const char *progname; //!< Program name for the program itself 149 150static uint32_t optionMask = 0; 151 152static uint64_t gsyncAdapterTS = 0; //!< The timestamp of the adapter 153static uint64_t gsyncFirstTS = 0; //!< The timestamp of the first packet in the file 154static uint64_t gsyncSyncTS = 0; //!< The timestamp of the oldset timestamp of all the files that it sent synchronized 155static uint64_t gsyncStartTime = 0; //!< The start time for the transmit to begin. 156static uint64_t gsyncOffset = 0; //!< The calculated transmit delay 157static uint64_t gsyncMaxTS = 0; //!< The timestamp of the last packet in the file. Used to calculate when the transmit is finish. 158static int gsyncAdapter = 0; //!< The adapter number for the adapter used to transmit the file. 159 160/* 161 * Defines used when parsing the commandline. 162 */ 163#define OPTION_FILE (1<<1) 164#define OPTION_PORTOFFSET (1<<2) 165#define OPTION_GSYNC (1<<3) 166#define OPTION_START (1<<4) 167#define OPTION_TIMESTAMP (1<<5) 168 169/** 170 * Table of valid options. 171 */ 172struct argparse_option arg_options[] = { 173 OPT_HELP(), 174 OPT_STRING( 'f', "file", &opt_file, "Specifies the file to replay.", NULL, 0, 0, "file name"), 175 OPT_INTEGER('p', "port", &opt_portOffset, "Port offset to port0 on the adapter on which the data\n" 176 "should be replayed.", NULL, 0, 0, "offset"), 177 OPT_UINT64('g', NULL, &gsyncSyncTS, "Enable global synchronization mode.\n" 178 "Earliest 10-ns ticks time stamp to synchronize with", NULL, 0, 0, "timestamp"), 179 OPT_STRING('s', NULL, &opt_start, "Absolute transmission begin time stamp.\n" 180 "Format: YYYY/MM/DD-HH:MM:SS, HH:MM:SS or HH:MM", NULL, 0, 0, "starttime"), 181 OPT_BOOLEAN('t', NULL, &opt_time, "Find the first time stamp (in 10-ns ticks) in the capture file.", NULL, 0, 0, NULL), 182 OPT_END(), 183}; 184 185/* 186 * The function called when user is pressing CTRL-C 187 */ 188#if defined(WIN32) || defined (WIN64) 189static BOOL WINAPI StopApplication(int sig) 190#else 191static void StopApplication(int sig __attribute__((unused))) 192#endif 193{ 194#ifdef WIN32 195 appRunning=0; 196 return TRUE; 197#else 198 if (sig == SIGINT) 199 appRunning=0; 200#endif 201} 202 203/* 204 * Display a header on the screen 205 */ 206static void DisplayProgramHeader(void) 207{ 208 int i; 209 210 printf("\nNapatech Example - %s\n", progname); 211 /* Print separator line */ 212 for(i=0; i<78; ++i) { 213 printf("="); 214 } 215 printf("\n"); 216} 217 218/* 219 * Display a footer on the screen 220 */ 221static void DisplayProgramFooter (void) 222{ 223 printf("\n"); 224} 225 226/** 227 * @brief Print a human readable time on the screen. 228 * 229 * This function is called to print a human readable 230 * time on the screen. Mainly used to print the transmit 231 * start time. 232 * 233 * @param[in] ts The timestamp to print 234 * 235 * @retval NT_SUCCESS Function succeded 236 * @retval != NT_SUCCESS Function failed 237 */ 238static void PrintTimeStamp(uint64_t ts) 239{ 240 time_t tmp_ts; 241 struct tm *loc_time; 242 char ts_buf[256]; 243 244 tmp_ts = (time_t) (ts / 100000000); 245 loc_time = localtime(&tmp_ts); 246 sprintf(ts_buf, "%04d/%02d/%02d-%02d:%02d:%02d.%09lu", 247 loc_time->tm_year+1900, loc_time->tm_mon+1, loc_time->tm_mday, 248 loc_time->tm_hour, loc_time->tm_min, loc_time->tm_sec, 249 (unsigned long) (ts % 100000000 * 10)); 250 printf("%s", ts_buf); 251} 252 253/** 254 * @brief Calculate the transmit offset 255 * 256 * This function is called to calculate the transmit offset. 257 * CalcOffset calculates the global offset to set on an adapter in global 258 * sync mode. The clocks of the adapters used must be synchronized. 259 * All time stamps supplieds as parameters must be in 10-ns ticks. 260 * The offset is calculated such that transmission starts on ts_start. 261 * 262 * The adapter implements the following logic in GSYNC mode. 263 * Let Tp denote the time stamp of a packet, t the current (running) 264 * time, and offset the GSYNC offset in the adapter. 265 * 266 * Tp > t + offset : wait, do not send the packet yet 267 * Tp <= t + offset : send the packet 268 * 269 * Replay calculates gsyncOffset a little differently, namely as 270 * 271 * t - Tp = gsyncOffset 272 * 273 * and hence 274 * 275 * Tp = -gsyncOffset + t 276 * 277 * If we insert this last equation into the right-hand side of the 278 * expression used in the adapter (Tp = t + offset), we get 279 * 280 * Tp = t + offset = -gsyncOffset + t 281 * 282 * and therefore 283 * 284 * offset = -gsyncOffset 285 * 286 * ReplayGS therefore uses the negated value of gsyncOffset as the 287 * offset value on the adapter. 288 * 289 * @param[in] ts_start The start time of the transmit 290 * @param[in] ts1 Timestamp 1 from either the local file or from 291 * a file sent from another server 292 * @param[in] ts2 Timestamp 2 from either the local file or from 293 * a file sent from another server 294 * 295 * @retval NT_SUCCESS Function succeded 296 * @retval != NT_SUCCESS Function failed 297 */ 298 299static uint64_t CalcOffset(uint64_t ts_start, uint64_t ts1, uint64_t ts2) 300{ 301 uint64_t offset = 0U; // offset value 302 uint64_t ts_min = 0U; // minimum time stamp of ts1 and ts2 303 304 ts_min = ts1 <= ts2 ? ts1 : ts2; // Chose the oldest of the timestamps 305 offset = ts_start - ts_min; // Calculate the offset 306 return offset; 307} 308 309/** 310 * @brief Gets the timestamp from the adapter 311 * 312 * This function is called to get the adapter timestamp 313 * 314 * @param[in] adapterNo Adapter number to get timestamp from 315 * @param[out] ts Pointer to the timestamp 316 * 317 * @retval NT_SUCCESS Function succeded 318 * @retval != NT_SUCCESS Function failed 319 */ 320static uint32_t GetAdapterTimestamp(int adapterNo, uint64_t *ts) 321{ 322 uint32_t status; 323 char errorBuffer[NT_ERRBUF_SIZE]; // Error buffer 324 NtConfigStream_t hStream; // Config stream handle 325 NtConfig_t configRead; // Config stream data container 326 327 // Open the config stream 328 if((status = NT_ConfigOpen(&hStream, "Replay")) != NT_SUCCESS) { 329 NT_ExplainError(status, errorBuffer, sizeof(errorBuffer)); 330 fprintf(stderr, "NT_ConfigOpen() failed: %s\n", errorBuffer); 331 NT_ConfigClose(hStream); 332 return status; 333 } 334 335 // Read the adapter timestamp 336 configRead.parm = NT_CONFIG_PARM_ADAPTER_TIMESTAMP; // Read adapter timestamp command 337 configRead.u.timestampRead.adapter = (uint8_t) adapterNo; // Adapter number of the adapter 338 if((status = NT_ConfigRead(hStream, &configRead)) != NT_SUCCESS) { 339 NT_ExplainError(status, errorBuffer, sizeof(errorBuffer)); 340 fprintf(stderr, "NT_ConfigRead() failed: %s\n", errorBuffer); 341 NT_ConfigClose(hStream); 342 return status; 343 } 344 *ts = configRead.u.timestampRead.data.nativeUnixTs; // Store the native unix version of the timestamp. 345 346 // Close the config stream 347 if((status = NT_ConfigClose(hStream)) != NT_SUCCESS) { 348 NT_ExplainError(status, errorBuffer, sizeof(errorBuffer)); 349 fprintf(stderr, "NT_ConfigOpen() failed: %s\n", errorBuffer); 350 return status; 351 } 352 return NT_SUCCESS; 353} 354 355/** 356 * @brief Sets Global Sync 357 * 358 * This function is called to enable or disable global sync 359 * 360 * @param[in] adapterNo Adapter number to get timestamp from 361 * @param[in] portMask Bitmask for ports to enable 362 * @param[in] offset Global sync offset 363 * 364 * @retval NT_SUCCESS Function succeded 365 * @retval != NT_SUCCESS Function failed 366 */ 367 368static uint32_t SetGlobalSync(int adapterNo, int portMask, uint64_t offset) 369{ 370 uint32_t status; 371 char errorBuffer[NT_ERRBUF_SIZE]; // Error buffer 372 NtConfigStream_t hStream; // Config stream handle 373 NtConfig_t configRead; // Config stream data container 374 375 // Open the config stream 376 if((status = NT_ConfigOpen(&hStream, "Replay")) != NT_SUCCESS) { 377 NT_ExplainError(status, errorBuffer, sizeof(errorBuffer)); 378 fprintf(stderr, "NT_ConfigOpen() failed: %s\n", errorBuffer); 379 NT_ConfigClose(hStream); 380 return status; 381 } 382 383 // Send the global sync offset to the adapteer 384 configRead.parm = NT_CONFIG_PARM_ADAPTER_GLOBAL_SYNC; // Global sync offset write command 385 configRead.u.globalSyncWrite.adapterNo = (uint8_t) adapterNo; // Adapter number 386 configRead.u.globalSyncWrite.data.portEnableMask = portMask; // Port enable mask for the 387 //ports where global sync should be enabled 388 configRead.u.globalSyncWrite.data.timeSynchronizedTxOffset = (~offset) + 1; // The calculated global sync offset. 389 // Note: The value must be written to 390 // the as two's complement. 391 configRead.u.globalSyncWrite.data.timeSynchronizedTxOffsetUpdate = portMask!=0?1:0; // Set to 1 when updating the offset. 392 if((status = NT_ConfigWrite(hStream, &configRead)) != NT_SUCCESS) { 393 NT_ExplainError(status, errorBuffer, sizeof(errorBuffer)); 394 fprintf(stderr, "NT_ConfigWrite() failed: %s\n", errorBuffer); 395 NT_ConfigClose(hStream); 396 return status; 397 } 398 399 // Close the config stream 400 if((status = NT_ConfigClose(hStream)) != NT_SUCCESS) { 401 NT_ExplainError(status, errorBuffer, sizeof(errorBuffer)); 402 fprintf(stderr, "NT_ConfigOpen() failed: %s\n", errorBuffer); 403 return status; 404 } 405 return NT_SUCCESS; 406} 407 408/** 409 * @brief Get the timestamp of the first packet in the file 410 * 411 * This function is called to get the timestamp of the first packet in the file 412 * 413 * @param[out] ts Pointer to variable holding the timestamp 414 * 415 * @retval 0 Function succeded 416 * @retval 1 Function failed 417 */ 418static int GetTimeStamp(uint64_t *ts) 419{ 420 int status; // Status variable 421 char errorBuffer[NT_ERRBUF_SIZE]; // Error buffer 422 NtNetStreamFile_t hNetFile; // Handle to the File stream 423 NtNetBuf_t hNetBufFile; // Net buffer container. Used to return packets from the file stream 424 struct NtNetBuf_s pktNetBuf; 425 426 // Open the capture file to to read the timestamp) 427 if((status = NT_NetFileOpen(&hNetFile, "replay", NT_NET_INTERFACE_SEGMENT, opt_file)) != NT_SUCCESS) { 428 // Get the status code as text 429 NT_ExplainError(status, errorBuffer, sizeof(errorBuffer)); 430 fprintf(stderr, "NT_NetFileOpen() failed: %s\n", errorBuffer); 431 return 1; 432 } 433 434 // Read the first segment of data from the file 435 if((status = NT_NetFileGet(hNetFile, &hNetBufFile)) != NT_SUCCESS) { 436 if(status == NT_STATUS_END_OF_FILE) { 437 fprintf(stderr, "The file %s has no data\n", opt_file); 438 return 1; 439 } 440 // Get the status code as text 441 NT_ExplainError(status, errorBuffer, sizeof(errorBuffer)); 442 fprintf(stderr, "NT_NetFileGet() failed: %s\n", errorBuffer); 443 return 1; 444 } 445 446 // Get a packet buffer pointer from the segment. 447 _nt_net_build_pkt_netbuf(hNetBufFile, &pktNetBuf); 448 449 // Store the timestamp 450 *ts = NT_NET_GET_PKT_TIMESTAMP((&pktNetBuf)); 451 452 // Close the file stream 453 NT_NetFileClose(hNetFile); 454 return 0; 455} 456 457/** 458 * @brief Parse the start time 459 * 460 * This function is called to parse the start time entered as input option 461 * 462 * @param[in] dts Pointer to string containing the start time 463 * @param[out] ts Pointer to variable to hold the converted start time 464 * 465 * @retval 0 Function succeded 466 * @retval -1 Function failed 467 */ 468static int ParseStartTime(const char *dts, uint64_t *ts) 469{ 470 char *rem_d; 471 struct tm tm; 472 time_t t; 473 struct tm *loc_time; 474 time_t now; 475 476 /* Reset tm structure because strptime is not required to initialize 477 all members variables. */ 478 memset(&tm, 0, sizeof(tm)); 479 /* Did user specify date+time in ISO format? */ 480 rem_d = strptime(dts, "%Y/%m/%d-%H:%M:%S", &tm); 481 if (!rem_d) { 482 /* Did user specify date and time in locale's representation? */ 483 rem_d = strptime(dts, "%x %X", &tm); 484 if (!rem_d) { 485 memset(&tm, 0, sizeof(tm)); 486 /* Did user specify (just) time in locale's representation? */ 487 rem_d = strptime(dts, "%X", &tm); 488 if (!rem_d) { 489 memset(&tm, 0, sizeof(tm)); 490 /* Did user specify only hour and minute? */ 491 rem_d = strptime(dts, "%H:%M:%S", &tm); 492 if (!rem_d) { 493 memset(&tm, 0, sizeof(tm)); 494 /* Did user specify only hour and minute? */ 495 rem_d = strptime(dts, "%H:%M", &tm); 496 497 if (!rem_d) 498 /* Failed to parse date/time */ 499 return -1; 500 } 501 } 502 503 /* User has only specified time, get yy/mm/dd from host, not adapter; 504 assume date on host and adapter are the same. */ 505 now = time(NULL); 506 loc_time = localtime(&now); 507 tm.tm_year = loc_time->tm_year; 508 tm.tm_mon = loc_time->tm_mon; 509 tm.tm_mday = loc_time->tm_mday; 510 } 511 } 512 513 t = timegm(&tm); 514 515 if (t == (time_t) -1) 516 return -1; 517 518 /* convert to 10-nsec ticks */ 519 *ts = t * 100000000;; 520 return 0; 521} 522 523/** 524 * @brief Print the transmission start time on the screen. 525 * 526 * This function is called to print the transmission start time on the screen. 527 * 528 * @param[in] adapter Adapter number 529 * @param[in] firstTS The time stamp of the first packet in the file 530 * @param[in] offset The calculated transmit offset 531 * 532 * @retval NT_SUCCESS Function succeded 533 * @retval != NT_SUCCESS Function failed 534 */ 535static int printTransmissionStart(int adapter, uint64_t firstTS, uint64_t offset) 536{ 537 uint64_t ts; 538 int64_t diff; 539 int status; 540 541 if ((status = GetAdapterTimestamp(adapter, &ts)) != NT_SUCCESS) { 542 fprintf(stderr, "Reading adapter timestamp failed\n"); 543 return status; 544 } 545 diff = (((int64_t)((firstTS + offset) - ts) / 100000000) + 1); 546 if(diff >= 0) { 547 printf("Transmission begins on "); 548 PrintTimeStamp(firstTS + offset); 549 printf(" (%lld sec) \r", (unsigned long long int)diff); 550 fflush(stdout); 551 } 552 return NT_SUCCESS; 553} 554 555int main(int argc, const char *argv[]) 556{ 557 int i; 558 const char *p; 559 uint64_t numPackets=0; // The number of packets replayed 560 uint64_t numBytes=0; // The number of bytes replayed 561 int firstPacket=1; // First packet to transmit 562 char errorBuffer[NT_ERRBUF_SIZE]; // Error buffer 563 int status; // Status variable 564 NtNetStreamFile_t hNetFile; // Handle to the File stream 565 NtNetBuf_t hNetBufFile; // Net buffer container. Used to return segments from the file stream 566 NtNetStreamTx_t hNetTx; // Handle to the TX stream 567 NtNetBuf_t hNetBufTx; // Net buffer container. Used when getting a transmit buffer 568 NtInfoStream_t hInfo; // Handle to a info stream 569 NtInfo_t infoRead; // Buffer to hold data from infostream 570 struct NtNetBuf_s pktNetBuf; // Packet netbuf structure. 571 struct argparse argparse; 572 573 // Strip path 574 i=(int)strlen(&argv[0][0]); 575 p=argv[0]; 576 for(;i!=0;i--) { 577#ifdef WIN32 578 if((p[i-1]=='\\') || (p[i-1]=='/')) { 579#else 580 if((p[i-1]=='.') || (p[i-1]=='\\') || (p[i-1]=='/')) { 581#endif 582 break; 583 } 584 } 585 progname = &argv[0][i]; // Store the daemon name so it can be used in Usage 586 DisplayProgramHeader(); 587 588 // *************************************************************** 589 // Parse the commandline to find the necessary global sync options 590 // *************************************************************** 591 argparse_init(&argparse, arg_options, usageText, 0); 592 argparse_parse(&argparse, argc, argv); 593 594 if (opt_file != NULL) { 595 optionMask |= OPTION_FILE; 596 } 597 if (opt_portOffset != -1) { 598 optionMask |= OPTION_PORTOFFSET; 599 } 600 if (gsyncSyncTS != 0) { 601 optionMask |= OPTION_GSYNC; 602 } 603 if (opt_start != NULL) { 604 if (ParseStartTime(opt_start, &gsyncStartTime) != 0) { 605 fprintf(stderr, ">>> Error: \"-s %s\" time format is invalid\n", opt_start); 606 return 1; 607 } 608 optionMask |= OPTION_START; 609 } 610 if (opt_time != 0) { 611 optionMask |= OPTION_TIMESTAMP; 612 } 613 614 // *********************************************** 615 // Validate input parameters 616 // *********************************************** 617 618 // We must always have a file. 619 if(!(optionMask & OPTION_FILE)) { 620 fprintf(stderr, "No file selected.\n"); 621 return 1; 622 } 623 624 // If -t only -f is allowed 625 if(optionMask & OPTION_TIMESTAMP) { 626 if (optionMask & ~(OPTION_FILE | OPTION_TIMESTAMP)) { 627 fprintf(stderr, "Only option -f is allowed with option -t.\n"); 628 return 1; 629 } 630 } 631 else { 632 // Must always have a port. 633 if(!(optionMask & OPTION_PORTOFFSET)) { 634 fprintf(stderr, "Missing portoffset -p.\n"); 635 return 1; 636 } 637 638 // Global sync start time must be defined 639 if(!(optionMask & OPTION_START)) { 640 fprintf(stderr, "Missing start time -s.\n"); 641 return 1; 642 } 643 } 644 645 // *********************************************** 646 // Initialize the NTAPI library and thereby check 647 // if NTAPI_VERSION can be used together with this 648 // library 649 // *********************************************** 650 651 if((status = NT_Init(NTAPI_VERSION)) != NT_SUCCESS) { 652 // Get the status code as text 653 NT_ExplainError(status, errorBuffer, sizeof(errorBuffer)); 654 fprintf(stderr, "NT_Init() failed: %s\n", errorBuffer); 655 return -1; 656 } 657 658 // ************************************* 659 // Get the timestamp of the first packet 660 // ************************************* 661 662 if((status = GetTimeStamp(&gsyncFirstTS)) != 0) { 663 return status; 664 } 665 666 if(optionMask & OPTION_TIMESTAMP) { 667 // We just want to get the first timestamp in the file. 668 // Print the timestamp and return 669 printf("Timestamp = %llu\n", (long long unsigned int)gsyncFirstTS); 670 return 0; 671 } 672 673// Set up ctrl+c handler 674#if defined(WIN32) || defined (WIN64) 675 SetConsoleCtrlHandler((PHANDLER_ROUTINE)StopApplication, TRUE); 676#else 677 struct sigaction newaction; // Ctrl+c handle 678 memset(&newaction, 0, sizeof(newaction)); 679 newaction.sa_handler = StopApplication; 680 if (sigaction(SIGINT, &newaction, NULL) < 0) { 681 fprintf(stderr, "Failed to register SIGINT sigaction.\n"); 682 exit(1); 683 } 684#endif 685 686 // ********************************** 687 // Read the port and adapter settings 688 // ********************************** 689 690 // Open the infostream. 691 if((status = NT_InfoOpen(&hInfo, "replay")) != NT_SUCCESS) { 692 NT_ExplainError(status, errorBuffer, sizeof(errorBuffer)); 693 fprintf(stderr, "NT_InfoOpen() failed: %s\n", errorBuffer); 694 return -1; 695 } 696 697 // Read the port and adapter settings 698 infoRead.cmd = NT_INFO_CMD_READ_PORT_V9; 699 infoRead.u.port_v9.portNo = (uint8_t) opt_portOffset; 700 if((status = NT_InfoRead(hInfo, &infoRead)) != NT_SUCCESS) { 701 NT_ExplainError(status, errorBuffer, sizeof(errorBuffer)); 702 fprintf(stderr, "NT_InfoOpen() failed: %s\n", errorBuffer); 703 NT_InfoClose(hInfo); 704 return -1; 705 } 706 707 NT_InfoClose(hInfo); 708 709 // The selected port offset must be equal to the first port on the adapter. 710 if(infoRead.u.port_v9.data.adapterInfo.portOffset != (uint32_t)opt_portOffset) { 711 fprintf(stderr, "\"-p %d\" is not a offset to a port0 on a physical adapter.\n", opt_portOffset); 712 return -1; 713 } 714 715 // Get the adapter number 716 gsyncAdapter = infoRead.u.port_v9.data.adapterNo; 717 718 // *********************************************** 719 // Check that the setup of the adapter is correct. 720 // *********************************************** 721 722 // Is sync supported? 723 if(infoRead.u.port_v9.data.adapterInfo.globalSync.supported == 0) { 724 fprintf(stderr, "Global sync is not supported by this adapter no. %d.\n", gsyncAdapter); 725 return -1; 726 } 727 728 // Only absolut TX timing mode can be used 729 if(infoRead.u.port_v9.data.adapterInfo.txTiming == NT_TX_TIMING_RELATIVE) { 730 fprintf(stderr, "Global sync cannot be used when running relative TX timing.\n"); 731 return -1; 732 } 733 734 // Only native unix timestamp can be used 735 if(infoRead.u.port_v9.data.adapterInfo.timestampType != NT_TIMESTAMP_TYPE_NATIVE_UNIX) { 736 fprintf(stderr, "Global sync can only be used when using \"native unix\" timestamps.\n"); 737 return -1; 738 } 739 740 // Adapter must be running the capture/replay profile for this to work. 741 if(infoRead.u.port_v9.data.adapterInfo.profile != NT_PROFILE_TYPE_CAPTURE_REPLAY) { 742 fprintf(stderr, "Please use the \"CaptureReplay\" profile.\n"); 743 return -1; 744 } 745 746 // ********************************** 747 // Setup Global sync for the adapter. 748 // ********************************** 749 750 // Get the adapter time stamp 751 if (GetAdapterTimestamp(gsyncAdapter, &gsyncAdapterTS) != NT_SUCCESS) { 752 fprintf(stderr, "Reading adapter timestamp failed\n"); 753 return -1; 754 } 755 756 // Validate start time is not in the past 757 if (gsyncStartTime <= gsyncAdapterTS) { 758 fprintf(stderr, "Error: Start time is in the past.\n"); 759 return -1; 760 } 761 762 // Sync timestamp -g is not defined. Use first timestamp in the 763 // packet as sync timestamp. This means the transmission will start 764 // with respect to the local file and not with respect to the other 765 // files in this synchronized transmit. 766 if(!(optionMask & OPTION_GSYNC)) { 767 gsyncSyncTS = gsyncFirstTS; 768 } 769 770 // Calculate the global sync offset 771 gsyncOffset = CalcOffset(gsyncStartTime, gsyncFirstTS, gsyncSyncTS); 772 773 // Write the offset to the adapter 774 if (SetGlobalSync(gsyncAdapter, 0xFF, gsyncOffset) != NT_SUCCESS) { 775 fprintf(stderr, "Setting Global Sync failed\n"); 776 return 1; 777 } 778 779 // Print the transmission start time on the screen. 780 if (printTransmissionStart(gsyncAdapter, gsyncFirstTS, gsyncOffset) != NT_SUCCESS) { 781 fprintf(stderr, "Printing start time failed\n"); 782 return 1; 783 } 784 785 // **************************** 786 // Start transmitting the file. 787 // **************************** 788 789 // Open the capture file to replay (captured with the capture example) 790 if((status = NT_NetFileOpen(&hNetFile, "FileStream", NT_NET_INTERFACE_SEGMENT, opt_file)) != NT_SUCCESS) { 791 // Get the status code as text 792 NT_ExplainError(status, errorBuffer, sizeof(errorBuffer)); 793 fprintf(stderr, "NT_NetFileOpen() failed: %s\n", errorBuffer); 794 return -1; 795 } 796 797 // Open a TX hostbuffer on NUMA node 0 that can transmit to port 0 798 if((status = NT_NetTxOpen(&hNetTx, "TxStreamPort0", (uint64_t)1<<opt_portOffset, NT_NETTX_NUMA_ADAPTER_HB, 0)) != NT_SUCCESS) { 799 // Get the status code as text 800 NT_ExplainError(status, errorBuffer, sizeof(errorBuffer)); 801 fprintf(stderr, "NT_NetTxOpen() failed: %s\n", errorBuffer); 802 return -1; 803 } 804 805 // Get segments from the file and transmit them to port 0 806 while(appRunning) { 807 NtNetFileRead_t data; 808 809 // Get the packet 810 if((status = NT_NetFileGet(hNetFile, &hNetBufFile)) != NT_SUCCESS) { 811 if(status == NT_STATUS_END_OF_FILE) { 812 // The file has no more data 813 break; 814 } 815 // Get the status code as text 816 NT_ExplainError(status, errorBuffer, sizeof(errorBuffer)); 817 fprintf(stderr, "NT_NetFileGet() failed: %s\n", errorBuffer); 818 return -1; 819 } 820 821 // Store the last timestamp in the read segment. We need this 822 // timestamp in order to know when the transmit is finished. 823 data.cmd = NT_NETFILE_READ_INFO_CMD_V1; 824 if((status = NT_NetFileRead(hNetFile, &data)) != NT_SUCCESS) { 825 // Get the status code as text 826 NT_ExplainError(status, errorBuffer, sizeof(errorBuffer)); 827 fprintf(stderr, "NtNetFileRead_t() failed: %s\n", errorBuffer); 828 return -1; 829 } 830 gsyncMaxTS = data.u.info_v1.lastTimestamp; 831 832 833 // Get a TX buffer for this segment 834 while ((status = NT_NetTxGet(hNetTx, &hNetBufTx, opt_portOffset, NT_NET_GET_SEGMENT_LENGTH(hNetBufFile), NT_NETTX_SEGMENT_OPTION_RAW, 1000)) != NT_SUCCESS) { 835 if(status != NT_STATUS_TIMEOUT) { 836 // Get the status code as text 837 NT_ExplainError(status, errorBuffer, sizeof(errorBuffer)); 838 fprintf(stderr, "NT_NetFileGet() failed: %s\n", errorBuffer); 839 return -1; 840 } 841 // Print the start time on the screen and count down to start. 842 if (printTransmissionStart(gsyncAdapter, gsyncFirstTS, gsyncOffset) != NT_SUCCESS) { 843 fprintf(stderr, "Printing start time failed\n"); 844 return 1; 845 } 846 if (appRunning == 0) { 847 goto CTRLEXIT; 848 } 849 } 850 851 // Copy the segment into the TX buffer 852 memcpy(NT_NET_GET_SEGMENT_PTR(hNetBufTx), NT_NET_GET_SEGMENT_PTR(hNetBufFile), NT_NET_GET_SEGMENT_LENGTH(hNetBufFile)); 853 854 // Build a packet netbuf structure 855 _nt_net_build_pkt_netbuf(hNetBufTx, &pktNetBuf); 856 857 if(firstPacket == 1) { 858 // The first packet must have txnow=0 and txsetclock=0 859 NT_NET_SET_PKT_TXNOW((&pktNetBuf), 0); // Gsync mode: txnow = 0 860 NT_NET_SET_PKT_TXSETCLOCK((&pktNetBuf), 0); // Gsync mode: txsetclock = 0 861 firstPacket = 0; 862 } 863 864 // Optional step. Here all packets are inspected before sent 865 if(NT_NET_GET_SEGMENT_LENGTH(hNetBufTx)) { 866 do { 867 // Just count the amount of packets and wire length 868 numPackets++; 869 numBytes+=NT_NET_GET_PKT_WIRE_LENGTH((&pktNetBuf)); 870 } while(_nt_net_get_next_packet(hNetBufTx, NT_NET_GET_SEGMENT_LENGTH(hNetBufTx), &pktNetBuf)>0); 871 } 872 873 // Release the TX buffer and the packets within the segment will be transmitted 874 if((status = NT_NetTxRelease(hNetTx, hNetBufTx)) != NT_SUCCESS) { 875 // Get the status code as text 876 NT_ExplainError(status, errorBuffer, sizeof(errorBuffer)); 877 fprintf(stderr, "NT_NetTxRelease() failed: %s\n", errorBuffer); 878 return -1; 879 } 880 881 // Release the file packet 882 if((status = NT_NetFileRelease(hNetFile, hNetBufFile)) != NT_SUCCESS) { 883 // Get the status code as text 884 NT_ExplainError(status, errorBuffer, sizeof(errorBuffer)); 885 fprintf(stderr, "NT_NetFileRelease() failed: %s\n", errorBuffer); 886 return -1; 887 } 888 889 // Print the number of packets transmitted to the screen 890 if(!(numPackets % 1000)) { 891 printf("Packets transmitted so far: %llu \r", 892 (unsigned long long) numPackets); 893 fflush(stdout); 894 } 895 } 896CTRLEXIT: 897 898 // Close the file stream 899 NT_NetFileClose(hNetFile); 900 901 // ************************************ 902 // Wait for the file to be transmitted. 903 // ************************************ 904 905 // We need to wait for the file to be transmitted before we can disable 906 // gsync again. Disabling the gsync before the file has been trasnitted, 907 // we cause the file to be transmitted immediately. 908 909 // To wait we are comparing the adapter timestamp with the last packet timstamp 910 // used in the file. When the adapter timestamp is greater than the last timestamp 911 // + the gsync offset. Then the file has been transmitted. 912 913 // Set app running so the user has a chance to cancel the wait. 914 appRunning=1; 915 while (appRunning) { 916 uint64_t ts; 917 int64_t diff; 918 919 if (GetAdapterTimestamp(gsyncAdapter, &ts) != NT_SUCCESS) { 920 fprintf(stderr, "Reading adapter timestamp failed\n"); 921 return 1; 922 } 923 if (ts > (gsyncMaxTS + gsyncOffset)) { 924 break; 925 } 926 927 diff = (((int64_t)((gsyncMaxTS + gsyncOffset) - ts) / 100000000) + 1); 928 if(diff < 0) { 929 // The file is transmitted. 930 break; 931 } 932 printf("Awaiting completion in %lld seconds \r", (long long int)diff); fflush(stdout); 933 // Sleep 1 sec 934#if defined(__linux__) || defined(__FreeBSD__) 935 sleep(1); 936#elif defined(WIN32) || defined (WIN64) 937 Sleep(1000); // sleep 1000 milliseconds = 1 second 938#endif 939 } 940 941 // Close the TX stream. If cancel on close is enabled. The remaning data will be 942 // canceled when the stream is closed. If cancel on close is not enabled the 943 // remaining packets will be transmitted according to the timestamp, when 944 // global sync is disabled. According to the timestamp means that the adapter 945 // will look at the timestamp before transmitting the packets. This will 946 // either cause the packets to be transmitted immediately or cause the to 947 // hang until the timestamp matches the adapter timestamp. In onther way 948 // it is unpredictable to say what will happen. 949 950 NT_NetTxClose(hNetTx); 951 952 // We need to disable Global Sync before we stop. 953 if (SetGlobalSync(gsyncAdapter, 0, 0) != NT_SUCCESS) { 954 appRunning = 0; 955 fprintf(stderr, "Disabling Global Sync failed\n"); 956 return 1; 957 } 958 959 printf("Done: %llu packets %llu bytes has been replayed \n", (unsigned long long int)numPackets, (unsigned long long int)numBytes); 960 DisplayProgramFooter(); 961 return 0; 962}