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