replay4ga_largefile_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_largefile/replay4ga_largefile_example.c Source File
replay4ga_largefile_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__)
47  #include <unistd.h> // sleep()
48  #include <stdatomic.h>
49 #endif
50 
51 #include <stdlib.h>
52 #include <signal.h>
53 #include <argparse.h>
54 
55 #define ONEMBIT (1000000ULL)
56 
57 /**
58  * Print command line info
59  */
60 static const char *usageText[] = {
61  "Syntax:\n"
62  "replay4ga_largefile_example [-h][-p <port no.>][-f <file name>]\n"
63  "Example: replay4ga_largefile_example -p 1 -f mycapture.cap\n",
64  NULL
65 };
66 
67 static int opt_port = 0;
68 static char *opt_filename;
69 
70 #if defined(WIN32) || defined (WIN64)
71 static volatile int appRunning = 1; // The application will run as long as appRunning == 1
72 #else
73 static atomic_int appRunning = 1; // The application will run as long as appRunning == 1
74 #endif
75 
76 
77 /**
78  * Table of valid options.
79  */
81  OPT_HELP(),
82  OPT_INTEGER('p', "port", &opt_port, "Tx port", NULL, 0, 0, "port number"),
83  OPT_STRING('f', "filename", &opt_filename, "File name\n", NULL, 0, 0, "value"),
84  OPT_END(),
85 };
86 
87 
88 /**
89  * The function called when user is pressing CTRL-C
90  */
91 #if defined(WIN32) || defined (WIN64)
92 static BOOL WINAPI StopApplication(int sig)
93 {
94  (void) sig;
95  appRunning = 0;
96  return TRUE;
97 }
98 #else
99 static void StopApplication(int sig)
100 {
101  if (sig == SIGINT)
102  appRunning = 0;
103 }
104 #endif
105 
106 
107 int
108 main(int argc, const char **argv)
109 {
110  char errorBuffer[NT_ERRBUF_SIZE]; // Error buffer
111  int status; // Status variable
112  NtNetStreamFile_t hNetFile; // Handle to the File stream
113  NtNetBuf_t hNetBufFile; // Net buffer container. Used to return segments from the file stream
114  NtNetStreamTx_t hNetTx; // Handle to the TX stream
115  NtNetBuf_t hNetBufTx; // Net buffer container. Used when getting a transmit buffer
116  NtInfoStream_t hInfo; // Handle to a info stream
117  NtInfo_t infoRead; // Buffer to hold data from infostream
118  NtConfigStream_t hConfig; // Handle to a config stream
119  NtConfig_t configRead; // Config stream data container
120  NtConfig_t configWrite; // Config stream data container
121  uint8_t adapterNo; // Adapter no
122  uint64_t firstPacketTS = 0; // Timestamp of first packet
123  uint64_t lastPacketTS = 0; // Timestamp of last packet
124  uint64_t adapterTS = 0; // Timestamp on the adapter
125  uint64_t timeDelta = 0; // Calculated time delta
126  uint64_t timeDeltaAdjust = 0; // Calculated time delta adjust
127  uint64_t speed; // Tx port speed
128  uint64_t tx_time_for_last_pkt; // Calculated transmission time for last packet
129  uint64_t segment_length = 0;
130  uint64_t last_segment_length = 0;
131 
132  struct argparse argparse;
133  uint64_t last_pkt_size = 0;
134 
135  // Register ctrl+c handler so we are able to stop again
136 #if defined(WIN32) || defined (WIN64)
137  SetConsoleCtrlHandler((PHANDLER_ROUTINE) StopApplication, TRUE);
138 #else
139  struct sigaction newaction; // Ctrl+c signal handler container
140  memset(&newaction, 0, sizeof(newaction));
141  newaction.sa_handler = StopApplication;
142  if (sigaction(SIGINT, &newaction, NULL) < 0) {
143  fprintf(stderr, "Failed to register SIGINT sigaction.\n");
144  exit(EXIT_FAILURE);
145  }
146 #endif
147 
148  argparse_init(&argparse, arg_options, usageText, 0);
149  argparse_parse(&argparse, argc, argv);
150 
151  // Initialize the NTAPI library and thereby check if NTAPI_VERSION can be used together with this library
152  if ((status = NT_Init(NTAPI_VERSION)) != NT_SUCCESS) {
153  // Get the status code as text
154  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
155  fprintf(stderr, "NT_Init() failed: %s\n", errorBuffer);
156  return -1;
157  }
158 
159  // Open the infostream.
160  if ((status = NT_InfoOpen(&hInfo, "replay")) != NT_SUCCESS) {
161  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
162  fprintf(stderr, "NT_InfoOpen() failed: %s\n", errorBuffer);
163  return -1;
164  }
165 
166  // Check whether or not TX is supported on the defined port
167  infoRead.cmd = NT_INFO_CMD_READ_PORT_V10;
168  infoRead.u.port_v10.portNo = (uint8_t)opt_port; // TX port
169  if ((status = NT_InfoRead(hInfo, &infoRead)) != NT_SUCCESS) {
170  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
171  fprintf(stderr, "NT_InfoRead() failed: %s\n", errorBuffer);
172  NT_InfoClose(hInfo);
173  return -1;
174  }
175 
176  // read port speed to use for calculating transmission time for last packet
177  switch (infoRead.u.port_v10.data.speed)
178  {
179 
180  case NT_LINK_SPEED_10M:
181  speed = 10ULL * ONEMBIT;
182  break;
183  case NT_LINK_SPEED_100M:
184  speed = 100ULL * ONEMBIT;
185  break;
186  case NT_LINK_SPEED_1G:
187  speed = 1000ULL * ONEMBIT;
188  break;
189  case NT_LINK_SPEED_10G:
190  speed = 10000ULL * ONEMBIT;
191  break;
192  case NT_LINK_SPEED_25G:
193  speed = 25000ULL * ONEMBIT;
194  break;
195  case NT_LINK_SPEED_40G:
196  speed = 40000ULL * ONEMBIT;
197  break;
198  case NT_LINK_SPEED_100G:
199  speed = 100000ULL * ONEMBIT;
200  break;
201  case NT_LINK_SPEED_200G:
202  speed = 200000ULL * ONEMBIT;
203  break;
204  default:
205  fprintf(stderr, "BUG: Unhandled port speed\n");
206  NT_InfoClose(hInfo);
207  return -1;
208  }
209 
210  if (infoRead.u.port_v10.data.state == NT_LINK_STATE_DOWN) {
211  fprintf(stderr, "Port %d has no link\n", opt_port);
212  NT_InfoClose(hInfo);
213  return -1;
214  }
215 
217  fprintf(stderr, "Transmit is not possible on the selected port %d\n", opt_port);
218  NT_InfoClose(hInfo);
219  return -1;
220  }
221 
222  // save adapter number of the TX port
223  adapterNo = infoRead.u.port_v10.data.adapterNo;
224  NT_InfoClose(hInfo);
225 
226  uint64_t totalSize = 0;
227  uint64_t totalSegments = 0;
228 
229  // Open the capture file to replay - for this purpose, just open it in PACKET mode and read packet by packet.
230  if ((status = NT_NetFileOpen(&hNetFile, "FileStream", NT_NET_INTERFACE_SEGMENT, opt_filename)) != NT_SUCCESS) {
231  // Get the status code as text
232  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
233  fprintf(stderr, "NT_NetFileOpen() failed: %s\n", errorBuffer);
234  return -1;
235  }
236 
237  // Issue get so we can read first packet timstamp
238  if ((status = NT_NetFileGet(hNetFile, &hNetBufFile)) != NT_SUCCESS) {
239  if (status == NT_STATUS_END_OF_FILE) {
240  fprintf(stderr, "The file %s has no data\n", opt_filename);
241  return -1;
242  }
243  }
244 
245  firstPacketTS = NT_NET_GET_PKT_TIMESTAMP(hNetBufFile) * 10;
246  totalSize += NT_NET_GET_SEGMENT_LENGTH(hNetBufFile);
247  totalSegments ++;
248 
249  do {
250  status = NT_NetFileGet(hNetFile, &hNetBufFile);
251  if (status != NT_STATUS_END_OF_FILE) {
252  lastPacketTS = NT_NET_GET_PKT_TIMESTAMP(hNetBufFile) * 10;
253  last_pkt_size = (uint64_t)NT_NET_GET_PKT_WIRE_LENGTH(hNetBufFile) + 20;
254  totalSize += NT_NET_GET_SEGMENT_LENGTH(hNetBufFile);
255  totalSegments ++;
256  }
257  }
258  while (status != NT_STATUS_END_OF_FILE);
259 
260  // Close the file again. We will open it again later to actually transmit packets.
261  NT_NetFileClose(hNetFile);
262 
263  // Open the config stream
264  if ((status = NT_ConfigOpen(&hConfig, "replay")) != NT_SUCCESS) {
265  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
266  fprintf(stderr, "NT_ConfigOpen() failed: %s\n", errorBuffer);
267  NT_ConfigClose(hConfig);
268  return -1;
269  }
270 
271  // Open the capture file to replay
272  if ((status = NT_NetFileOpen(&hNetFile, "FileStream", NT_NET_INTERFACE_SEGMENT, opt_filename)) != NT_SUCCESS) {
273  // Get the status code as text
274  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
275  fprintf(stderr, "NT_NetFileOpen() failed: %s\n", errorBuffer);
276  return -1;
277  }
278 
279  uint64_t requiredHostbuffer = totalSize / (1024UL * 1024UL);
280  printf("Required host buffer: %lu MiB (Actual size: %lu MiB)\n", totalSegments, requiredHostbuffer);
281  // Open a TX hostbuffer from TX host buffer pool on the NUMA node of the adapter with the defined port
282  //if ((status = NT_NetTxOpen_v3(&hNetTx, "TxStreamPort", (0x1 << opt_port), NT_NETTX_NUMA_ADAPTER_HB, totalSize, NT_PACKET_DESCRIPTOR_TYPE_NT, NT_TIMESTAMP_TYPE_NATIVE_UNIX)) != NT_SUCCESS) {
283  if ((status = NT_NetTxOpen(&hNetTx, "TxStreamPort", 1ULL << opt_port, NT_NETTX_NUMA_ADAPTER_HB, (uint32_t)totalSegments)) != NT_SUCCESS) {
284  // Get the status code as text
285  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
286  fprintf(stderr, "NT_NetTxOpen() failed: %s\n", errorBuffer);
287  return -1;
288  }
289 
290  //Read the adapter time
292  configRead.u.timestampRead.adapter = adapterNo;
293  if ((status = NT_ConfigRead(hConfig, &configRead)) != NT_SUCCESS) {
294  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
295  fprintf(stderr, "NT_ConfigRead() failed: %s\n", errorBuffer);
296  NT_ConfigClose(hConfig);
297  return -1;
298  }
299  adapterTS = configRead.u.timestampRead.data.nativeUnixTs * 10; // Convert 10ns ticks to 1ns
300 
301  // last frame time stamp - first frame time stamp + estimated transmission time for last frame
302  tx_time_for_last_pkt = (last_pkt_size * 8U * 100000000ULL) / speed;
303  timeDeltaAdjust = lastPacketTS - firstPacketTS + tx_time_for_last_pkt;
304 
305  // Start transfer 10 seconds from now
306  timeDelta = (adapterTS - firstPacketTS) + 10000000000UL;
307  printf("Transfer starts in 10 seconds from now\n");
308 
309  // Configure transmit on timestamp
311  configWrite.u.transmitOnTimestamp.portNo = (uint8_t) opt_port;
312  configWrite.u.transmitOnTimestamp.data.timeDelta = timeDelta;
313  configWrite.u.transmitOnTimestamp.data.timeDeltaAdjust = timeDeltaAdjust;
314  configWrite.u.transmitOnTimestamp.data.enable = true;
315  configWrite.u.transmitOnTimestamp.data.forceTxOnTs = false;
316  if ((status = NT_ConfigWrite(hConfig, &configWrite)) != NT_SUCCESS) {
317  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
318  fprintf(stderr, "NT_ConfigWrite() failed: %s\n", errorBuffer);
319  NT_ConfigClose(hConfig);
320  return 0;
321  }
322 
323  printf("Capture file duration: %.01fs\n", (double)timeDeltaAdjust / 1000000000);
324  printf("NOTICE: First repetition may be slower than capture rate!\n");
325 
326  NtNetBuf_t *netBufs = malloc(totalSegments * sizeof(NtNetBuf_t));
327  if (netBufs == NULL) {
328  fprintf(stderr, "Could not allocate tx segment pointer array\n");
329  return -1;
330  }
331 
332  for (uint32_t i = 0; i < totalSegments; i++) {
333  if ((status = NT_NetFileGet(hNetFile, &hNetBufFile)) != NT_SUCCESS) {
334  // Get the status code as text
335  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
336  fprintf(stderr, "NT_NetFileGet() failed: %s\n", errorBuffer);
337  goto ExitError;
338  }
339 
340  if ((status = NT_NetTxGet(hNetTx, &netBufs[i], (uint32_t)opt_port, NT_NET_GET_SEGMENT_LENGTH(hNetBufFile), NT_NETTX_SEGMENT_OPTION_RAW, -1))) {
341  // Get the status code as text
342  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
343  fprintf(stderr, "NT_NetTxGet() failed: %s\n", errorBuffer);
344  goto ExitError;
345  }
346 
347  if (i == totalSegments - 1)
348  last_segment_length = NT_NET_GET_SEGMENT_LENGTH(hNetBufFile);
349  else
350  segment_length = NT_NET_GET_SEGMENT_LENGTH(hNetBufFile);
351 
352  memcpy(NT_NET_GET_SEGMENT_PTR(netBufs[i]), NT_NET_GET_SEGMENT_PTR(hNetBufFile), NT_NET_GET_SEGMENT_LENGTH(hNetBufFile));
353 
354  if ((status = NT_NetFileRelease(hNetFile, hNetBufFile)) != NT_SUCCESS) {
355  // Get the status code as text
356  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
357  fprintf(stderr, "NT_NetFileRelease() failed: %s\n", errorBuffer);
358  goto ExitError;
359  }
360 
361  // Due to a potential bug, tx segments are released right after filling,
362  // resulting in potentially slower initial transmit
363  if ((status = NT_NetTxRelease(hNetTx, netBufs[i])) != NT_SUCCESS) {
364  // Get the status code as text
365  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
366  fprintf(stderr, "NT_NetTxRelease() failed: %s\n", errorBuffer);
367  goto ExitError;
368  }
369  }
370 
371  // Close the file stream
372  NT_NetFileClose(hNetFile);
373 
374  // // Release the loaded tx segments
375  // for (uint32_t i = 0; i < totalSegments; i++) {
376  // if ((status = NT_NetTxRelease(hNetTx, netBufs[i])) != NT_SUCCESS) {
377  // // Get the status code as text
378  // NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
379  // fprintf(stderr, "NT_NetTxRelease() failed: %s\n", errorBuffer);
380  // return -1;
381  // }
382  // }
383 
384  // Get segments from the file and transmit them to defined port
385  while (appRunning == 1) {
386  for (uint32_t i = 0; i < totalSegments; i++) {
387 
388  // Get a TX buffer for this segment
389  do {
390  uint64_t segmentLen = i + 1 == totalSegments ? last_segment_length : segment_length;
391  status = NT_NetTxGet(hNetTx, &hNetBufTx, (uint32_t)opt_port, segmentLen, NT_NETTX_SEGMENT_OPTION_RAW, 100);
392 
393  if ((status != NT_SUCCESS) && (status != NT_STATUS_TIMEOUT)) {
394  // Get the status code as text
395  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
396  fprintf(stderr, "NT_NetFileGet() failed: %s\n", errorBuffer);
397  goto ExitError;
398  }
399  } while (status == NT_STATUS_TIMEOUT);
400 
401  if (i == 0) {
402  NT_NET_SET_PKT_TXSETCLOCK(hNetBufTx, 1);
403  }
404 
405  // Release the TX buffer and the packets within the segment will be transmitted
406  if ((status = NT_NetTxRelease(hNetTx, hNetBufTx)) != NT_SUCCESS) {
407  // Get the status code as text
408  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
409  fprintf(stderr, "NT_NetTxRelease() failed: %s\n", errorBuffer);
410  goto ExitError;
411  }
412  }
413  }
414 
415  // Close the TX stream
416  NT_NetTxClose(hNetTx);
417 
418  // Disable transmit on timestamp
420  configWrite.u.transmitOnTimestamp.portNo = (uint8_t) opt_port;
421  configWrite.u.transmitOnTimestamp.data.timeDelta = 0;
422  configWrite.u.transmitOnTimestamp.data.enable = false;
423  configWrite.u.transmitOnTimestamp.data.forceTxOnTs = false;
424  if ((status = NT_ConfigWrite(hConfig, &configWrite)) != NT_SUCCESS) {
425  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
426  fprintf(stderr, "NT_ConfigWrite() failed: %s\n", errorBuffer);
427  NT_ConfigClose(hConfig);
428  goto ExitError;
429  }
430 
431  // Close the config stream
432  NT_ConfigClose(hConfig);
433 
434  free(netBufs);
435  return 0;
436 
437 ExitError:
438  if (netBufs) {
439  free(netBufs);
440  }
441  return -1;
442 }