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