replayGS_example.c Source File

Reference Documentation

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