timestamp_inject_example.cpp Source File

Reference Documentation

Platform
Napatech SmartNIC
Content Type
Reference Information
Capture Software Version
Link™ Capture Software 12.15
Napatech Software Suite: examples/net/timestamp_inject/timestamp_inject_example.cpp Source File
timestamp_inject_example.cpp
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 /**
44  * @example net/timestamp_inject/timestamp_inject_example.cpp
45  * @section timestamp_inject_example_description Description
46  *
47  * INFO: This is an 4GA TX timestamp inject and FCS generation example.
48  *
49  * This source file is an example of how to control TX time stamp inject
50  * and FCS generation per packet using dynamic descriptor 3.
51  *
52  * One bit in the TX packet descriptor is used to control
53  * TX time stamp inject, i.e. if the packet descriptor bit is
54  * 1 a TX time stamp will be inserted in the packet.
55  *
56  * Two bits in the TX packet descriptor is used to control
57  * FCS generation, i.e.
58  * 0: Insert good FCS,
59  * 1: Insert bad FCS,
60  * 2: Reserved
61  * 3: Don't change FCS
62  *
63  * This example requires the following setting in ntservive.ini
64  *
65  * TimestampFormat = UNIX_NS
66  *
67  * TimestampInjectStaticOffset = 80
68  *
69  * TimestampInjectDynamicOffset = TSI_DYN_SOF
70  *
71  * PacketDescriptor = NT
72  *
73  * This program consists of a TX and RX part.
74  *
75  * The TX part will transmit 6 packets in a loop.
76  * The number of loops is a command line option.
77  * To start the TX part use the -t <txport> option.
78  *
79  * The six packets in a loop are based on the same
80  * packet template. Time stamp inject and FCS generation
81  * are then controlled per packet to be:
82  *
83  * txPacket(hNetTx, NO_TS_INJECT, FCS_UNCHANGED);
84  *
85  * txPacket(hNetTx, NO_TS_INJECT, FCS_GOOD);
86  *
87  * txPacket(hNetTx, NO_TS_INJECT, FCS_BAD);
88  *
89  * txPacket(hNetTx, TS_INJECT, FCS_UNCHANGED);
90  *
91  * txPacket(hNetTx, TS_INJECT, FCS_GOOD);
92  *
93  * txPacket(hNetTx, TS_INJECT, FCS_BAD);
94  *
95  * The RX part will receive packets from one port.
96  * It will check for the a TX time stamp in received
97  * packets and - if present - calculate and print
98  * the TX-to-RX latency.
99  * The RX part also dumps the received packet.
100  * To start the RX part use the -r <rxport> option.
101  *
102  * Example:
103  *
104  * timestamp_inject_example -t 0 -n 5 -r 1
105  *
106  * will transmit on port 0 and receive on port 1.
107  * The transmitter will execute the loop 5 times,
108  * i.e. 30 packets will be transmitted.
109  *
110  */
111 
112 // Include this in order to access the Napatech API
113 #include <nt.h>
114 
115 #include <iostream>
116 #include <thread>
117 #include <unistd.h>
118 #include <stdlib.h>
119 #include <signal.h>
120 #include <atomic>
121 
122 #if defined(WIN32) || defined(WIN64)
123  #define snprintf(dst, ...) _snprintf_s((dst), _countof(dst), __VA_ARGS__)
124 #endif
125 
126 // Intentional global
127 // The application will run as long as appRunning == 1
128 static std::atomic_int appRunning {1};
129 
130 // The struct below represents the TX dynamic descriptor
131 // used by this application
132 //
133 // The struct is based on a copy of Dynamic descriptor 3
134 // It is modified with the location of the command bits
135 // for controlling L3/L4 checksum handling, time stamp
136 // inject handling and FCS handling.
137 //
138 // This example uses 12 bits of the color_hi field
139 // for these command bits. In other words - the
140 // normal color_hi:28 has been replaced by the
141 // 12 command bits and a color_hi_unused:16
142 //
143 // note: This example doesn't use the L3/L4 checksum
144 // command bits. See examples/net/checksum for an
145 // example of L3/L4 checksum control.
146 struct Dyn3_tx_descriptor {
147  uint64_t capLength:14;
148  uint64_t wireLength:14;
149  uint64_t color_lo:14;
150  uint64_t rxPort:6;
151  uint64_t descrFormat:8;
152  uint64_t descrLength:6;
153  uint64_t tsColor:1;
154  uint64_t ntDynDescr:1;
155 
156  uint64_t timestamp;
157  // Location of control bits in the packet descriptor:
158  uint64_t frame_type:4; // Frame type: 4 bits starting at bit offset 128
159  uint64_t checksum_cmd:5; // L3/L4 check sum command, 5 bits starting at bit offset 132
160  uint64_t tsiCmd:1; // Timestamp inject command, 1 bits starting at bit offset 137
161  uint64_t fcsCmd:2; // FCS command, 1 bits starting at bit offset 138
162  uint64_t color_hi_unused:16; // The remaing 16 unused bits of color_hi
163 
164  uint64_t offset0:10;
165  uint64_t offset1:10;
166 };
167 
168 
170 {
171  switch (descrType)
172  {
174  printf("Descriptor type is PCAP.\n");
175  break;
176 
178  printf("Descriptor type is NT.\n");
179  break;
180 
182  printf("Descriptor type is NT extended.\n");
183  break;
184 
186  printf("Descriptor type is Dynamic\n");
187  break;
188 
190  default:
191  printf("Unknown descriptor type.\n");
192  break;
193  }
194 }
195 
196 
197 
198 /*
199  The packet reader thread is responsible for
200  reading packets from a single RX stream
201 */
203 {
204 public:
205 
206  uint64_t rxPort;
207  uint32_t streamId;
208  std::thread thr;
209 
210  PacketReaderThread(uint64_t port) : rxPort(port) {
211  streamId = (uint32_t) port;
212  printf("Created PacketReaderThread, port=%lu, stream=%u\n", rxPort, streamId);
213  }
214 
215 
216  void processPackets(void) {
217  NtNetStreamRx_t hNetRx = NULL; // Handle to the RX stream
218  NtNetBuf_t hNetBuf; // Net buffer container.
219  char errorBuffer[NT_ERRBUF_SIZE];
220  int status;
221 
222  status = NT_NetRxOpen(&hNetRx, "PacketReader", NT_NET_INTERFACE_PACKET, streamId, -1);
223  if (status != 0) {
224  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
225  printf("NT_NetRxOpen() failed: %s\n", errorBuffer);
226  appRunning = 0;
227  return;
228  }
229 
230  printf("Receiving packets on stream %u\n", streamId);
231 
232  while (appRunning == 1)
233  {
234  if ((status = NT_NetRxGetNextPacket(hNetRx, &hNetBuf, 100)) == NT_SUCCESS)
235  {
236  printf("-------------------------------------------------\n");
237  printf("Packet received:\n");
238 
239  NtPacketDescriptorType_e descrType = NT_NET_GET_PKT_DESCRIPTOR_TYPE(hNetBuf);
240  printDescriptor(descrType);
241  if (descrType != NT_PACKET_DESCRIPTOR_TYPE_NT)
242  {
243  printf("Error: RX descriptor expected to be NT_PACKET_DESCRIPTOR_TYPE_NT\n");
244  printf(" Shutting down...\n");
245  appRunning = 0;
246  break;
247  }
248 
249  uint64_t captureLength = NT_NET_GET_PKT_CAP_LENGTH(hNetBuf);
250  if (captureLength < (16 + 80 + 2 + 8)) // Descriptor + Timestamp Inject Static Offset + 2 byte compensation + 8 byte Time stamp
251  {
252  printf("Packet is too small. Can't extract time stamp\n");
253  }
254  else
255  {
256  unsigned char * data = (unsigned char *) NT_NET_GET_PKT_L2_PTR(hNetBuf);
257  uint64_t txTime = *((uint64_t*)(data + 80 + 2));
258 
259  if (txTime == 0)
260  {
261  printf("No TX time stamp in packet.\n");
262  }
263  else
264  {
265  uint64_t rxTime = NT_NET_GET_PKT_TIMESTAMP(hNetBuf);
266  printf("txTime=%lu, rxTime=%lu, latency=%lu\n", txTime, rxTime, rxTime - txTime);
267  }
268 
269  // Dump packet
270  uint64_t wireLength = NT_NET_GET_PKT_WIRE_LENGTH(hNetBuf);
271  for (uint64_t g = 0; g < wireLength; ++g)
272  {
273  if ((g % 16) == 0) printf("\n");
274  printf("%02X ", *(data + g));
275  }
276  printf("\n");
277  }
278  }
279  else if ((status != NT_STATUS_TIMEOUT) && (status != NT_STATUS_TRYAGAIN))
280  {
281  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
282  printf("NT_NetRxGet() failed: %s\n", errorBuffer);
283  appRunning = 0;
284  break;
285  }
286  }
287 
288  NT_NetRxClose(hNetRx);
289  }
290 
291  void run(void) {
292  thr = std::thread(&PacketReaderThread::processPackets, this);
293  }
294 
295  void join(void) {
296  if (thr.joinable())
297  {
298  thr.join();
299  }
300  }
301 
302 };
303 
304 // Packet template: Ethernet, IPv4, TCP
305 const unsigned char examplePacket[] =
306 {
307  0x94, 0xC6, 0x91, 0x1C, 0x68, 0x1D, 0x94, 0xC6, 0x91, 0x1C, 0x68, 0xC3, 0x08, 0x00, 0x45, 0x00,
308  0x00, 0x78, 0x00, 0x01, 0x00, 0x00, 0x40, 0x06, 0xB5, 0xCF, 0xC0, 0xA8, 0x00, 0x02, 0x01, 0x02,
309  0x03, 0x04, 0x00, 0x14, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x02,
310  0x20, 0x00, 0x42, 0xF7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
311  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
312  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
313  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
314  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
315  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
316 };
317 
318 
319 /*
320  The packet transmitter thread is responsible for
321  transmitting on a single port.
322 
323  The packets will have different combinations
324  of time stamp inject and FCS calculation
325  via command bits in the TX packet decriptor
326 */
328 {
329 public:
330 
331  uint64_t transmittedPackets {0};
332  uint32_t txPort;
333  int64_t txLoops;
334  std::thread thr;
335 
336  // Time stamp inject command bit values
337  const unsigned char NO_TS_INJECT = 0;
338  const unsigned char TS_INJECT = 1;
339 
340  // FCS command bits values
341  const unsigned char FCS_GOOD = 0;
342  const unsigned char FCS_BAD = 1;
343  const unsigned char FCS_RESERVED = 2;
344  const unsigned char FCS_UNCHANGED = 3;
345 
346  PacketTransmitterThread(uint64_t port, int64_t loops) : txPort((uint32_t)port), txLoops(loops) {
347  printf("Created PacketTransmitterThread port=%u\n", txPort);
348  }
349 
350  void run(void) {
351  thr = std::thread(&PacketTransmitterThread::transmitPackets, this);
352  }
353 
354  void join(void) {
355  if (thr.joinable())
356  {
357  thr.join();
358  }
359  }
360 
361 private:
362 
363  void txPacket(NtNetStreamTx_t hNetTx, unsigned char tsi, unsigned char fcs) {
364  uint32_t sleep_time = 1000000;
365 
366  NtNetBuf_t hNetBufTx; // Net buffer container. Used when getting a transmit buffer
367  int status;
368  char errorBuffer[NT_ERRBUF_SIZE];
369  const unsigned char * pData = examplePacket;
370  size_t pktSize = sizeof examplePacket;
371 
372  if ((status = NT_NetTxGet(hNetTx, &hNetBufTx, txPort, pktSize, NT_NETTX_PACKET_OPTION_DYN, 100)) != NT_SUCCESS)
373  {
374  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
375  fprintf(stderr, "NT_NetTxGet() failed: %s\n", errorBuffer);
376  appRunning = 0;
377  return;
378  }
379 
380  //
381  memcpy(NT_NET_GET_PKT_L2_PTR(hNetBufTx), pData, pktSize);
382 
383  auto packet_ptr = NT_NET_GET_PKT_DESCR_PTR_DYN3(hNetBufTx);
384  auto overlay_ptr = reinterpret_cast<Dyn3_tx_descriptor*>(packet_ptr);
385 
386  packet_ptr->offset0 = 14; // Point to start of Layer 3, i.e. set to size of Ethernet header
387  packet_ptr->offset1 = 34; // Point to start of Layer 4, i.e. set to size of Ethernet header + IPv4 header with no options
388  overlay_ptr->frame_type = 0x4; // IPv4 packet carrying TCP
389  overlay_ptr->checksum_cmd = 0; // L3/L4 checksum_command, set to "do nothing"
390 
391  overlay_ptr->tsiCmd = tsi & 1; // Set the time stamp inject command bits
392  overlay_ptr->fcsCmd = fcs & 3; // Set the FCS command bits
393 
394  // Release the TX buffer and the packet will be transmitted
395  if ((status = NT_NetTxRelease(hNetTx, hNetBufTx)) != NT_SUCCESS)
396  {
397  // Get the status code as text
398  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
399  fprintf(stderr, "NT_NetTxRelease() failed: %s\n", errorBuffer);
400  appRunning = 0;
401  return;
402  }
403  ++transmittedPackets;
404  if (sleep_time)
405  {
406  usleep(sleep_time);
407  }
408  }
409 
410  void transmitPackets(void) {
411 
412  char errorBuffer[NT_ERRBUF_SIZE];
413  int status;
414  NtNetStreamTx_t hNetTx; // Handle to the TX stream
415 
416  printf("============================================\n");
417  printf("Transmitter configuration\n");
418  printf("============================================\n");
419  printf("TX port : %u\n", txPort);
420  printf("============================================\n");
421 
422  NtNetTxAttr_t txAttr;
423 
424  NT_NetTxOpenAttrInit(&txAttr);
425  NT_NetTxOpenAttrSetName(&txAttr, "Time stamp Inject and FCS example");
426  NT_NetTxOpenAttrSetPortMask(&txAttr, static_cast<uint64_t>(1U) << txPort);
429 
430  // Set the position of the various descriptor command
431  // in accordance with struct Dyn3_tx_descriptor
432  status = NT_NetTxOpenAttrSetDescriptorPosFrameType(&txAttr, true, 128);
433  if (status != NT_SUCCESS)
434  {
435  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
436  fprintf(stderr, "NT_NetTxOpenAttrSetDescriptorPosFrameType failed: %s\n", errorBuffer);
437  exit(1);
438  }
439 
440  status = NT_NetTxOpenAttrSetDescriptorPosChecksumCmd(&txAttr, true, 132);
441  if (status != NT_SUCCESS)
442  {
443  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
444  fprintf(stderr, "NT_NetTxOpenAttrSetDescriptorPosChecksumCmd failed: %s\n", errorBuffer);
445  exit(1);
446  }
447 
448  status = NT_NetTxOpenAttrSetTxtDescriptorPosTimestampInject(&txAttr, true, 137);
449  if (status != NT_SUCCESS)
450  {
451  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
452  fprintf(stderr, "NT_NetTxOpenAttrSetTxtDescriptorPosTimestampInjectCmd failed: %s\n", errorBuffer);
453  exit(1);
454  }
455 
456  status = NT_NetTxOpenAttrSetTxtDescriptorPosFcs(&txAttr, true, 138);
457  if (status != NT_SUCCESS)
458  {
459  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
460  fprintf(stderr, "NT_NetTxOpenAttrSetTxtDescriptorPosFcs failed: %s\n", errorBuffer);
461  exit(1);
462  }
463 
464  // Open the TX network stream
465  status = NT_NetTxOpen_Attr(&hNetTx, &txAttr);
466  if (status != NT_SUCCESS) {
467  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
468  fprintf(stderr, "NT_NetTxOpen() failed: %s\n", errorBuffer);
469  return;
470  }
471 
472  // Main TX loop
473  while (appRunning == 1 && txLoops != 0)
474  {
475  if (txLoops > 0) --txLoops;
476 
477  // Transmit 6 packets
478  txPacket(hNetTx, NO_TS_INJECT, FCS_UNCHANGED);
479  txPacket(hNetTx, NO_TS_INJECT, FCS_GOOD);
480  txPacket(hNetTx, NO_TS_INJECT, FCS_BAD);
481  txPacket(hNetTx, TS_INJECT, FCS_UNCHANGED);
482  txPacket(hNetTx, TS_INJECT, FCS_GOOD);
483  txPacket(hNetTx, TS_INJECT, FCS_BAD);
484  }
485 
486  printf("Packets transmitted %lu\n ", transmittedPackets);
487  printf("Shutting down TX\n");
488 
489  if ((status = NT_NetTxClose(hNetTx)) != NT_SUCCESS)
490  {
491  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
492  fprintf(stderr, "NT_NetTxClose() failed: %s\n", errorBuffer);
493  return;
494  }
495 
496  appRunning = 0;
497  }
498 };
499 
500 /**
501  * The function called when user is pressing CTRL-C
502  */
503 #if defined(WIN32) || defined (WIN64)
504 static BOOL WINAPI StopApplication(int sig)
505 {
506  (void) sig;
507  appRunning = 0;
508  return TRUE;
509 }
510 #else
511 static void StopApplication(int sig)
512 {
513  if (sig == SIGINT)
514  appRunning = 0;
515 }
516 #endif
517 
518 
519 void printError(int status, const char* formatstr)
520 {
521  char errorBuffer[NT_ERRBUF_SIZE];
522 
523  // Retrieve the textual description of the status code
524  NT_ExplainError(status, errorBuffer, (uint32_t)(sizeof(errorBuffer)-1));
525 
526  // Log to stderr
527  fprintf(stderr, formatstr, errorBuffer);
528 }
529 
530 // Setup RX filter
531 void applyNtpl(uint64_t rxPort)
532 {
533  NtConfigStream_t hCfgStream;
534  char ntplStr[400];
535  int status;
536 
537  snprintf(ntplStr, 400, "assign[streamid=%lu] = Port==%lu", rxPort, rxPort);
538 
539  // Open a config stream to assign a filter to a stream ID.
540  if ((status = NT_ConfigOpen(&hCfgStream, "flowtest")) != NT_SUCCESS) {
541  printError(status, "NT_ConfigOpen() failed: %s\n");
542  exit(1);
543  }
544 
545  printf("\nDoing NTPL:\n");
546  NtNtplInfo_t ntplInfo;
547  printf("%s\n\n", ntplStr);
548  if ((status = NT_NTPL(hCfgStream, ntplStr, &ntplInfo, NT_NTPL_PARSER_VALIDATE_NORMAL)) != NT_SUCCESS) {
549  printError(status, "NT_NTPL() failed: %s\n");
550  exit(1);
551  }
552 
553  NT_ConfigClose(hCfgStream);
554 }
555 
556 bool featuresAvailable(uint64_t txPort)
557 {
558  int status;
559  NtInfoStream_t hInfo;
560  char errorBuffer[NT_ERRBUF_SIZE];
561  NtInfo_t infoRead;
562 
563  // Open the infostream.
564  if ((status = NT_InfoOpen(&hInfo, "tsi")) != NT_SUCCESS) {
565  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
566  fprintf(stderr, "NT_InfoOpen() failed: %s\n", errorBuffer);
567  return false;
568  }
569 
570  // Check whether or not Time stamp inject is supported on the defined port
571  infoRead.cmd = NT_INFO_CMD_READ_PORT_V10;
572  infoRead.u.port_v10.portNo = 0xFF & txPort;
573  if ((status = NT_InfoRead(hInfo, &infoRead)) != NT_SUCCESS) {
574  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
575  fprintf(stderr, "NT_InfoRead() failed: %s\n", errorBuffer);
576  NT_InfoClose(hInfo);
577  return false;
578  }
579 
580  NT_InfoClose(hInfo);
581 
582  if (
588  )
589  {
590  return false;
591  }
592 
593  return true;
594 }
595 
596 static void usage(const char *argv0)
597 {
598  fprintf(stderr, "\n");
599  fprintf(stderr, "Usage: %s [options]\n", argv0);
600  fprintf(stderr, "\n");
601  fprintf(stderr, "This program is an example of how to control TX time stamp inject\n");
602  fprintf(stderr, "and FCS generation per packet using dynamic descriptor 3.\n");
603  fprintf(stderr, "\n");
604  fprintf(stderr, " Options:\n"
605  " -r <rx port> : RX port.\n"
606  " -t <tx port> : TX port.\n"
607  " -n <tx loops> : Number of TX loops\n"
608  " -h : Print help message\n");
609  fprintf(stderr, "\n");
610 }
611 
612 int main(int argc, char **argv)
613 {
614  int opt;
615  int64_t txLoops = -1;
616  uint64_t txPort = 0;
617  uint64_t rxPort = 0;
618  uint64_t txActive = false;
619  uint64_t rxActive = false;
620 
621  PacketReaderThread* rxThread = nullptr;
622  PacketTransmitterThread* txThread = nullptr;
623 
624  char *endptr;
625  while ((opt = getopt(argc, argv, "n:t:r:")) != -1) {
626  switch (opt) {
627  case 'r':
628  rxPort = strtoul(optarg, &endptr, 10);
629  rxActive = true;
630  if (*optarg == '\0' || *endptr != '\0') {
631  fprintf(stderr, "Could not parse RX port, i.e. -r option\n");
632  exit(1);
633  }
634  break;
635  case 't':
636  txPort = strtoul(optarg, &endptr, 10);
637  txActive = true;
638  if (*optarg == '\0' || *endptr != '\0') {
639  fprintf(stderr, "Could not parse TX port, i.e. -t option\n");
640  exit(1);
641  }
642  break;
643  case 'n':
644  txLoops = strtol(optarg, &endptr, 10);
645  if (*optarg == '\0' || *endptr != '\0') {
646  fprintf(stderr, "Could not parse number of TX loops, , i.e. -n option\n");
647  exit(1);
648  }
649  break;
650  case 'h':
651  usage(argv[0]);
652  exit(0);
653  break;
654  default:
655  usage(argv[0]);
656  exit(1);
657  }
658  }
659 
660  // Register ctrl+c handler so we are able to stop again
661 #if defined(WIN32) || defined (WIN64)
662  SetConsoleCtrlHandler((PHANDLER_ROUTINE)StopApplication, TRUE);
663 #else
664  struct sigaction newaction; // Ctrl+c signal handler container
665  memset(&newaction, 0, sizeof(newaction));
666  newaction.sa_handler = StopApplication;
667  if (sigaction(SIGINT, &newaction, NULL) < 0) {
668  fprintf(stderr, "Failed to register SIGINT sigaction.\n");
669  exit(EXIT_FAILURE);
670  }
671 #endif
672 
673  NT_Init(NTAPI_VERSION);
674 
675  if (!featuresAvailable(txPort))
676  {
677  printf("\nRequired Test&Measurement features not supported on port %lu\n", txPort);
678  return 0;
679  }
680 
681  if (txActive)
682  {
683  txThread = new PacketTransmitterThread(txPort, txLoops);
684  }
685  if (rxActive)
686  {
687  applyNtpl(rxPort);
688  rxThread = new PacketReaderThread(rxPort);
689  }
690 
691  if (rxThread == nullptr && txThread == nullptr)
692  {
693  printf("\nUse at least one of the options -t, -r\n");
694  return 0;
695  }
696 
697  if (rxThread != nullptr)
698  {
699  rxThread->run();
700  }
701 
702  usleep(1000000);
703 
704  if (txThread != nullptr)
705  {
706  txThread->run();
707  }
708 
709  printf("Threads started. Press CTRL-C to terminate program.\n");
710  while (appRunning == 1)
711  {
712  usleep(1000);
713  };
714 
715  if (txThread != nullptr)
716  {
717  txThread->join();
718  delete txThread;
719 
720  if (rxThread != nullptr && appRunning == 1)
721  {
722  printf("TX thread stopped. Use CTRL-C to terminate RX thread\n");
723  }
724  }
725 
726  if (rxThread != nullptr)
727  {
728  rxThread->join();
729  delete rxThread;
730  }
731 
732  NT_Done();
733 
734  return 0;
735 }