replay4ga_loop_example.c Source File

Reference Documentation

Platform
Napatech SmartNIC
Content Type
Reference Information
Capture Software Version
Link™ Capture Software 12.15
Napatech Software Suite: examples/net/replay4GA_loop/replay4ga_loop_example.c Source File
replay4ga_loop_example.c
Go to the documentation of this file.
1 /*
2  *
3  * Copyright 2025 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 "NA-0009 Software License Agreement.pdf"
18  *
19  * 4. Redistributions of source code must retain this copyright notice,
20  * list of conditions and the following disclaimer.
21  *
22  * THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTIES, EXPRESS OR
23  * IMPLIED, AND NAPATECH DISCLAIMS ALL IMPLIED WARRANTIES INCLUDING ANY
24  * IMPLIED WARRANTY OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, OR OF
25  * FITNESS FOR A PARTICULAR PURPOSE. TO THE EXTENT NOT PROHIBITED BY
26  * APPLICABLE LAW, IN NO EVENT SHALL NAPATECH BE LIABLE FOR PERSONAL INJURY,
27  * OR ANY INCIDENTAL, SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES WHATSOEVER,
28  * INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF PROFITS, CORRUPTION OR
29  * LOSS OF DATA, FAILURE TO TRANSMIT OR RECEIVE ANY DATA OR INFORMATION,
30  * BUSINESS INTERRUPTION OR ANY OTHER COMMERCIAL DAMAGES OR LOSSES, ARISING
31  * OUT OF OR RELATED TO YOUR USE OR INABILITY TO USE NAPATECH SOFTWARE OR
32  * SERVICES OR ANY THIRD PARTY SOFTWARE OR APPLICATIONS IN CONJUNCTION WITH
33  * THE NAPATECH SOFTWARE OR SERVICES, HOWEVER CAUSED, REGARDLESS OF THE THEORY
34  * OF LIABILITY (CONTRACT, TORT OR OTHERWISE) AND EVEN IF NAPATECH HAS BEEN
35  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. SOME JURISDICTIONS DO NOT ALLOW
36  * THE EXCLUSION OR LIMITATION OF LIABILITY FOR PERSONAL INJURY, OR OF
37  * INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS LIMITATION MAY NOT APPLY TO YOU.
38  *
39  *
40 
41  */
42 
43 // Include this in order to access the Napatech API
44 #include <nt.h>
45 
46 #if defined(__linux__) || defined(__FreeBSD__) || (__MINGW32__)
47  #include <unistd.h> // sleep()
48  #include <stdatomic.h>
49 #endif
50 
51 #if defined(_MSC_VER)
52  #include <timesupport.h>
53 #endif
54 
55 #include <stdlib.h>
56 #include <signal.h>
57 #include <argparse.h>
58 
59 #define ONEMBIT (1000000ULL)
60 
61 /**
62  * Print command line info
63  */
64 static const char *usageText[] = {
65  "Syntax:\n"
66  "replay4ga_loop_example [-h][-p <port no.>][-f <file name>][-n <number of iterations> [-s <replay speed in % of capture>]\n"
67  "Example: replay4ga_loop_example -p 1 -f mycapture.cap -n 10 -s 200\n",
68  NULL
69 };
70 
71 static int opt_port = 0;
72 static char *opt_filename;
73 static int opt_num_repetitions = 1;
74 static int opt_scale = 100;
75 
76 #if defined(WIN32) || defined (WIN64)
77 static volatile int appRunning = 1; // The application will run as long as appRunning == 1
78 #else
79 static atomic_int appRunning = 1; // The application will run as long as appRunning == 1
80 #endif
81 
82 /**
83  * Table of valid options.
84  */
86  OPT_HELP(),
87  OPT_INTEGER('p', "port", &opt_port, "Tx port", NULL, 0, 0, "port number"),
88  OPT_STRING('f', "filename", &opt_filename, "File name\n", NULL, 0, 0, "value"),
89  OPT_INTEGER('n', "repetitions", &opt_num_repetitions, "Number of repetitions\n", NULL, 0, 0, "value"),
90  OPT_INTEGER('s', "scale", &opt_scale, "Replay speed in % of captured speed\n", NULL, 0, 0, "value"),
91  OPT_END(),
92 };
93 
94 
95 /**
96  * The function called when user is pressing CTRL-C
97  */
98 #if defined(WIN32) || defined (WIN64)
99 static BOOL WINAPI StopApplication(int sig)
100 #else
101 static void StopApplication(int sig)
102 #endif
103 {
104 #ifdef WIN32
105  appRunning = 0;
106  return TRUE;
107 #else
108  if (sig == SIGINT)
109  appRunning = 0;
110 #endif
111 }
112 
113 // Struct pcap_timespec_s is the time specification for PCAP. For PCAP_NANO,
114 // the ts_usec field is in nanoseconds instead of microseconds.
116  uint32_t ts_sec;
117  uint32_t ts_usec;
118 };
119 
120 static uint64_t
121 convertToUnixNsTs(const struct NtConfigTimestampReadData_s *data)
122 {
123  const struct pcap_timespec_s *pcap_ts = (const struct pcap_timespec_s *)&data->ts;
124  switch (data->tsType) {
126  return pcap_ts->ts_sec * 1000000000ULL + pcap_ts->ts_usec;
128  return data->ts;
129  default:
130  // default to nativeUnixTs when nanosecond resolution unavailable
131  // this is Unix Time with 10ns resolution.
132  return data->nativeUnixTs * 10;
133  }
134 }
135 
136 int
137 main(int argc, const char **argv)
138 {
139  int numPackets = 0; // The number of packets replayed
140  int numBytes = 0; // The number of bytes replayed
141  char errorBuffer[NT_ERRBUF_SIZE]; // Error buffer
142  int status; // Status variable
143  NtNetStreamFile_t hNetFile; // Handle to the File stream
144  NtNetBuf_t hNetBufFile; // Net buffer container. Used to return segments from the file stream
145  NtNetStreamTx_t hNetTx; // Handle to the TX stream
146  NtNetBuf_t hNetBufTx; // Net buffer container. Used when getting a transmit buffer
147  NtInfoStream_t hInfo; // Handle to a info stream
148  NtInfo_t infoRead; // Buffer to hold data from infostream
149  NtConfigStream_t hConfig; // Handle to a config stream
150  NtConfig_t configRead; // Config stream data container
151  NtConfig_t configWrite; // Config stream data container
152  struct NtNetBuf_s pktNetBuf; // Packet netbuf structure.
153  uint8_t adapterNo; // Adapter no
154  uint64_t firstPacketTS = 0; // Timestamp of first packet
155  uint64_t lastPacketTS = 0; // Timestamp of last packet
156  uint64_t adapterTS = 0; // Timestamp on the adapter
157  uint64_t adapterEndTS = 0; // Estimated Timestamp for completion
158  uint64_t timeDelta = 0; // Calculated time delta
159  uint64_t timeDeltaAdjust = 0; // Calculated time delta adjust
160  uint64_t speed; // Tx port speed
161  uint64_t tx_time_for_last_pkt; // Calculated transmission time for last packet
162 
163  struct argparse argparse;
164 
165  int set_tx_clock = 1;
166  unsigned last_pkt_size = 0;
167 
168  // Register ctrl+c handler so we are able to stop again
169 #if defined(WIN32) || defined (WIN64)
170  SetConsoleCtrlHandler((PHANDLER_ROUTINE) StopApplication, TRUE);
171 #else
172  struct sigaction newaction; // Ctrl+c signal handler container
173  memset(&newaction, 0, sizeof(newaction));
174  newaction.sa_handler = StopApplication;
175  if (sigaction(SIGINT, &newaction, NULL) < 0) {
176  fprintf(stderr, "Failed to register SIGINT sigaction.\n");
177  exit(EXIT_FAILURE);
178  }
179 #endif
180 
181  argparse_init(&argparse, arg_options, usageText, 0);
182  argparse_parse(&argparse, argc, argv);
183 
184  // Sanitize...
185  if (opt_scale == 0)
186  opt_scale = 100;
187 
188  // printf("filename=%s scale=%d port=%d iter=%d\n",
189  // opt_filename, opt_scale, opt_port, opt_num_repetitions);
190 
191  // Initialize the NTAPI library and thereby check if NTAPI_VERSION can be used together with this library
192  if ((status = NT_Init(NTAPI_VERSION)) != NT_SUCCESS) {
193  // Get the status code as text
194  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
195  fprintf(stderr, "NT_Init() failed: %s\n", errorBuffer);
196  return -1;
197  }
198 
199  // Open the infostream.
200  if ((status = NT_InfoOpen(&hInfo, "replay")) != NT_SUCCESS) {
201  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
202  fprintf(stderr, "NT_InfoOpen() failed: %s\n", errorBuffer);
203  return -1;
204  }
205 
206  // Check whether or not TX is supported on the defined port
207  infoRead.cmd = NT_INFO_CMD_READ_PORT_V10;
208  infoRead.u.port_v10.portNo = (uint8_t)opt_port; // TX port
209  if ((status = NT_InfoRead(hInfo, &infoRead)) != NT_SUCCESS) {
210  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
211  fprintf(stderr, "NT_InfoRead() failed: %s\n", errorBuffer);
212  NT_InfoClose(hInfo);
213  return -1;
214  }
215 
216  // read port speed to use for calculating transmission time for last packet
217  switch (infoRead.u.port_v10.data.speed)
218  {
219 
220  case NT_LINK_SPEED_10M:
221  speed = 10ULL * ONEMBIT;
222  break;
223  case NT_LINK_SPEED_100M:
224  speed = 100ULL * ONEMBIT;
225  break;
226  case NT_LINK_SPEED_1G:
227  speed = 1000ULL * ONEMBIT;
228  break;
229  case NT_LINK_SPEED_10G:
230  speed = 10000ULL * ONEMBIT;
231  break;
232  case NT_LINK_SPEED_25G:
233  speed = 25000ULL * ONEMBIT;
234  break;
235  case NT_LINK_SPEED_40G:
236  speed = 40000ULL * ONEMBIT;
237  break;
238  case NT_LINK_SPEED_100G:
239  speed = 100000ULL * ONEMBIT;
240  break;
241  default:
242  fprintf(stderr, "BUG: Unhandled port speed\n");
243  NT_InfoClose(hInfo);
244  return -1;
245  }
246 
247  if (infoRead.u.port_v10.data.state == NT_LINK_STATE_DOWN) {
248  fprintf(stderr, "Port %d has no link\n", opt_port);
249  NT_InfoClose(hInfo);
250  return -1;
251  }
252 
254  fprintf(stderr, "Transmit is not possible on the selected port %d\n", opt_port);
255  NT_InfoClose(hInfo);
256  return -1;
257  }
258 
259  // save adapter number of the TX port
260  adapterNo = infoRead.u.port_v10.data.adapterNo;
261  NT_InfoClose(hInfo);
262 
263  // Open the capture file to replay - for this purpose, just open it in PACKET mode and read packet by packet.
264  NtNetFileAttr_t attr;
265  NT_NetFileOpenAttrInit(&attr);
266  NT_NetFileOpenAttrSetName(&attr, "FileStream");
270  if ((status = NT_NetFileOpen_Attr(&hNetFile, opt_filename, &attr)) != NT_SUCCESS) {
271  // Get the status code as text
272  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
273  fprintf(stderr, "NT_NetFileOpen() failed: %s\n", errorBuffer);
274  return -1;
275  }
276 
277  // Issue get so we can read first packet timstamp
278  if ((status = NT_NetFileGet(hNetFile, &hNetBufFile)) != NT_SUCCESS) {
279  if (status == NT_STATUS_END_OF_FILE) {
280  fprintf(stderr, "The file %s has no data\n", opt_filename);
281  return -1;
282  }
283  }
284 
285  firstPacketTS = NT_NET_GET_PKT_TIMESTAMP(hNetBufFile);
286 
287  do {
288  status = NT_NetFileGet(hNetFile, &hNetBufFile);
289  if (status != NT_STATUS_END_OF_FILE) {
290  lastPacketTS = NT_NET_GET_PKT_TIMESTAMP(hNetBufFile);
291  last_pkt_size = (unsigned)NT_NET_GET_PKT_WIRE_LENGTH(hNetBufFile) + 20;
292  }
293  }
294  while (status != NT_STATUS_END_OF_FILE);
295 
296 
297  //printf("firstTS=%lu lastTS=%lu\n",
298  // firstPacketTS, lastPacketTS);
299 
300  // don't try to scale unless asked to
301  if (opt_scale != 100) {
302  firstPacketTS = (firstPacketTS / (unsigned)opt_scale) * 100;
303  lastPacketTS = (lastPacketTS / (unsigned)opt_scale) * 100;
304  }
305 
306  // Close the file again. We will open it again later to actually transmit packets.
307  NT_NetFileClose(hNetFile);
308 
309  // Open the config stream
310  if ((status = NT_ConfigOpen(&hConfig, "replay")) != NT_SUCCESS) {
311  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
312  fprintf(stderr, "NT_ConfigOpen() failed: %s\n", errorBuffer);
313  NT_ConfigClose(hConfig);
314  return -1;
315  }
316 
317  // Read the adapter time
319  configRead.u.timestampRead.adapter = adapterNo;
320  if ((status = NT_ConfigRead(hConfig, &configRead)) != NT_SUCCESS) {
321  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
322  fprintf(stderr, "NT_ConfigRead() failed: %s\n", errorBuffer);
323  NT_ConfigClose(hConfig);
324  return -1;
325  }
326 
327  // Convert the adapter timestamp into unix nanoseconds.
328  // Nanosecond precision is only available if TimestampFormat = UNIX_NS or
329  // TimestampFormat = PCAP_NS as defined in ntservice.ini.
330  // If TimestampFormat is set to anything else, it will derive the
331  // nanosecond timestamp from the NATIVE timestamp, which is measured
332  // in 10-ns ticks, and hence lose a small amount of precision.
333  adapterTS = convertToUnixNsTs(&configRead.u.timestampRead.data);
334 
335  // last frame time stamp - first frame time stamp + estimated transmission time for last frame
336  tx_time_for_last_pkt = (last_pkt_size * 8U * 100000000ULL) / speed;
337  timeDeltaAdjust = lastPacketTS - firstPacketTS + tx_time_for_last_pkt;
338 
339  // Calculate time delta - we add a bit of time to give ourselves some headroom
340  timeDelta = (adapterTS - firstPacketTS) + 1000000000 - timeDeltaAdjust;
341 
342  // add a bit of extra margin such that we don't terminate too soon.
343  adapterEndTS = adapterTS + 2000000000 + (lastPacketTS - firstPacketTS) * (uint64_t)opt_num_repetitions;
344 
345  // Configure transmit on timestamp
347  configWrite.u.transmitOnTimestamp.portNo = (uint8_t) opt_port;
348  configWrite.u.transmitOnTimestamp.data.timeDelta = timeDelta;
349  configWrite.u.transmitOnTimestamp.data.timeDeltaAdjust = timeDeltaAdjust;
350  configWrite.u.transmitOnTimestamp.data.enable = true;
351  configWrite.u.transmitOnTimestamp.data.forceTxOnTs = true;
352  if ((status = NT_ConfigWrite(hConfig, &configWrite)) != NT_SUCCESS) {
353  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
354  fprintf(stderr, "NT_ConfigWrite() failed: %s\n", errorBuffer);
355  NT_ConfigClose(hConfig);
356  return 0;
357  }
358 
359  // Open the capture file to replay
360  if ((status = NT_NetFileOpen(&hNetFile, "FileStream", NT_NET_INTERFACE_SEGMENT, opt_filename)) != NT_SUCCESS) {
361  // Get the status code as text
362  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
363  fprintf(stderr, "NT_NetFileOpen() failed: %s\n", errorBuffer);
364  return -1;
365  }
366 
367  // Open a TX hostbuffer from TX host buffer pool on the NUMA node of the adapter with the defined port
368  if ((status = NT_NetTxOpen(&hNetTx, "TxStreamPort", 1ULL << opt_port, NT_NETTX_NUMA_ADAPTER_HB, 0)) != NT_SUCCESS) {
369  // Get the status code as text
370  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
371  fprintf(stderr, "NT_NetTxOpen() failed: %s\n", errorBuffer);
372  return -1;
373  }
374 
375  printf("Transmitting from file %s\n", opt_filename);
376 
377  // Get segments from the file and transmit them to defined port
378  while (appRunning == 1 && (opt_num_repetitions > 0)) {
379  // Get the packet
380  if ((status = NT_NetFileGet(hNetFile, &hNetBufFile)) != NT_SUCCESS) {
381  if (status == NT_STATUS_END_OF_FILE) {
382  // The file has no more data
383  // NB: When NT_STATUS_END_OF_FILE is returned the internal file offset that been reset making it possible to call NT_NetFileGet() again without having to close and open the file
385  set_tx_clock = 1;
386  continue;
387  }
388  // Get the status code as text
389  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
390  fprintf(stderr, "NT_NetFileGet() failed: %s\n", errorBuffer);
391  return -1;
392  }
393 
394  // Get a TX buffer for this segment
395  do {
396  status = NT_NetTxGet(hNetTx, &hNetBufTx, (uint32_t)opt_port, NT_NET_GET_SEGMENT_LENGTH(hNetBufFile), NT_NETTX_SEGMENT_OPTION_RAW, 100);
397  if ((status != NT_SUCCESS) && (status != NT_STATUS_TIMEOUT)) {
398  // Get the status code as text
399  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
400  fprintf(stderr, "NT_NetFileGet() failed: %s\n", errorBuffer);
401  return -1;
402  }
403  } while (status == NT_STATUS_TIMEOUT);
404 
405  // Copy the segment into the TX buffer
406  memcpy(NT_NET_GET_SEGMENT_PTR(hNetBufTx), NT_NET_GET_SEGMENT_PTR(hNetBufFile), NT_NET_GET_SEGMENT_LENGTH(hNetBufFile));
407 
408  // Build a packet netbuf structure
409  _nt_net_build_pkt_netbuf(hNetBufTx, &pktNetBuf);
410 
411  if (set_tx_clock) {
412  NT_NET_SET_PKT_TXSETCLOCK(hNetBufTx, 1);
413  // only do set clock for the first packet in each iteration
414  set_tx_clock = 0;
415  }
416 
417  // Optional step. Adjust tmiestamp for all packets are inspected before sent
418  if (NT_NET_GET_SEGMENT_LENGTH(hNetBufTx)) {
419  do {
420  uint64_t timestamp;
421  // Just count the amount of packets and wire length
422  numPackets++;
423  numBytes += NT_NET_GET_PKT_WIRE_LENGTH((&pktNetBuf));
424 
425  if (opt_scale != 100) {
426  /* simple timestamp adjustment according to scale */
427  timestamp = NT_NET_GET_PKT_TIMESTAMP((&pktNetBuf));
428  timestamp /= (unsigned)opt_scale;
429  timestamp *= 100;
430  NT_NET_SET_PKT_TIMESTAMP((&pktNetBuf), timestamp);
431  }
432 
433  } while (_nt_net_get_next_packet(hNetBufTx, NT_NET_GET_SEGMENT_LENGTH(hNetBufTx), &pktNetBuf) > 0);
434  }
435 
436 
437  // Release the TX buffer and the packets within the segment will be transmitted
438  if ((status = NT_NetTxRelease(hNetTx, hNetBufTx)) != NT_SUCCESS) {
439  // Get the status code as text
440  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
441  fprintf(stderr, "NT_NetTxRelease() failed: %s\n", errorBuffer);
442  return -1;
443  }
444  // Release the file packet
445  if ((status = NT_NetFileRelease(hNetFile, hNetBufFile)) != NT_SUCCESS) {
446  // Get the status code as text
447  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
448  fprintf(stderr, "NT_NetFileRelease() failed: %s\n", errorBuffer);
449  return -1;
450  }
451  }
452 
453  if (appRunning == 1) {
454  printf("Done scheduling packets - Waiting for completion...\n");
455 
456  // Wait until all packets should have been sent
457  while (appRunning == 1) {
458  // Read the adapter time
460  configRead.u.timestampRead.adapter = adapterNo;
461  if ((status = NT_ConfigRead(hConfig, &configRead)) != NT_SUCCESS) {
462  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
463  fprintf(stderr, "NT_ConfigRead() failed: %s\n", errorBuffer);
464  NT_ConfigClose(hConfig);
465  return 0;
466  }
467 
468  // Convert the timestamp into unix nanoseconds
469  adapterTS = convertToUnixNsTs(&configRead.u.timestampRead.data);
470 
471  if (adapterTS > adapterEndTS) {
472  // The last packet should be sending now
473  break;
474  }
475 
476  sleep(1);
477  }
478  }
479 
480  // Close the file stream
481  NT_NetFileClose(hNetFile);
482 
483  // Close the TX stream
484  NT_NetTxClose(hNetTx);
485 
486  // Disable transmit on timestamp
488  configWrite.u.transmitOnTimestamp.portNo = (uint8_t) opt_port;
489  configWrite.u.transmitOnTimestamp.data.timeDelta = 0;
490  configWrite.u.transmitOnTimestamp.data.enable = false;
491  configWrite.u.transmitOnTimestamp.data.forceTxOnTs = false;
492  if ((status = NT_ConfigWrite(hConfig, &configWrite)) != NT_SUCCESS) {
493  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
494  fprintf(stderr, "NT_ConfigWrite() failed: %s\n", errorBuffer);
495  NT_ConfigClose(hConfig);
496  return 0;
497  }
498 
499  // Close the config stream
500  NT_ConfigClose(hConfig);
501 
502  if (appRunning == 1) {
503  printf("Done: %d packets %d bytes replayed\n", numPackets, numBytes);
504  }
505 
506  return 0;
507 }