transmit_on_timestamp_setclock_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/transmit_on_timestamp_setclock/transmit_on_timestamp_setclock_example.c Source File
transmit_on_timestamp_setclock_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 /**
44  * @example net/transmit_on_timestamp_setclock/transmit_on_timestamp_setclock_example.c
45  * @section transmit_on_timestamp_setclock_example_description Description
46  *
47  * This source file is an example of how to transmit a sequence of packets
48  * on timestamp multiple times, but instead of manually reconfiguring the
49  * delta for each iteration, TXSETCLOCK is used to advance the delta with
50  * the duration of a single replay iteration. An example application of this
51  * functionality would be where one wishes to replay a single capture file
52  * multiple times.
53  *
54  * This example will transmit 100000 packets of 296 bytes from port 0 at a rate
55  * of approximately 5 Mb/s. The packet contains an incrementing 32bit pattern.
56  *
57  * The transmit on timestamp functionality is also available in segment mode.
58  * See the @ref net/transmit_segment/transmit_segment_example.c example code
59  * to see how to use the segment interface.
60  *
61  * The following NTAPI functions are used:
62  * - @ref NT_Init()
63  * - @ref NT_ConfigOpen()
64  * - @ref NT_ConfigRead()
65  * - @ref NT_ConfigWrite()
66  * - @ref NT_ConfigClose()
67  * - @ref NT_NetTxOpen_v2()
68  * - @ref NT_NetTxGet()
69  * - @ref NT_NET_SET_PKT_TIMESTAMP()
70  * - @ref NT_NET_SET_PKT_TXSETCLOCK()
71  * - @ref NT_NET_GET_PKT_L2_PTR()
72  * - @ref NT_NetTxRelease()
73  * - @ref NT_NetTxClose()
74  * - @ref NT_Done()
75  * - @ref NT_ExplainError()
76  *
77  * @section transmit_on_timestamp_setclock_example_prerequisites Prerequisites
78  * - This example is only compatible with adapters supporting 4GA transmit on
79  * timestamp.
80  * - The ntservice.ini must have at least one HostBuffersTx defined. Below is
81  * an example of a minimum ini-file. It will create a 4MB TX hostbuffer from
82  * NUMA node 0.
83  *
84  * @code
85  * [Adapter0]
86  * AdapterType = NT20E2
87  * BusId = 00:0a:00.00
88  * HostBuffersTx = [1,4,0]
89  * @endcode
90  *
91  * @section transmit_on_timestamp_setclock_example_flow Program flow
92  * @{
93  * The following is required to transmit packets:
94  * - \#include/nt.h - Applications/Tools only need to include @ref
95  * nt.h to obtain prototypes, macros etc. from NTAPI.
96  * - @ref NT_Init(@ref NTAPI_VERSION) - Initialize the NTAPI
97  * library. @ref NTAPI_VERSION is a define that describes the version
98  * of the API described in the header files included by @ref
99  * nt.h. NT_Init() will ask the NTAPI library to convert return data
100  * to the @ref NTAPI_VERSION if possible. This will ensure that
101  * applications can run on NTAPI libraries of newer versions.
102  * - @ref NT_ConfigOpen() - Open the config stream.
103  * - @ref NT_ConfigRead() - Use the config stream to read the current adapter
104  * time.
105  * - @ref NT_ConfigWrite() - Use the config stream to configure transmit on
106  * timestamp, using the retrieved adapter time.
107  * - @ref NT_NetTxOpen() - Open a hostbuffer than can transmit packets to port 0.
108  * - @ref NT_NetTxOpen_v2() - Open a hostbuffer than can transmit packets to port 0.
109  * - @ref NT_NetTxGet() - Get an empty tx buffer. This will get a 296 byte
110  * wire length packet buffer that will be sent onto port 0 when the packet
111  * buffer is released, and the timestamp has been reached on the adapter.
112  * - @ref NT_NET_GET_PKT_L2_PTR() is used to get the L2 pointer to the tx
113  * buffer, this is where the payload is placed.
114  * - @ref NT_NET_SET_PKT_TXSETCLOCK() is used to enable txsetclock on the
115  * first packet in the replay sequence.
116  * - @ref NT_NET_SET_PKT_TIMESTAMP() is used to set the timestamp of the
117  * packet
118  * - @ref NT_NetTxRelease() - Release the tx packet buffer. Once a tx
119  * buffer is released it will be transmitted
120  * - @ref NT_ConfigRead() - Read the adapter timestamp to check if the last
121  * packet should have been transmitted. We need to do this in order to avoid
122  * disabling transmit on timestamp too early.
123  * - @ref NT_ConfigWrite() - Disable transmit on timestamp.
124  * - @ref NT_ConfigClose() - Close the configuration stream.
125  * - @ref NT_NetTxClose() - Close the TX stream.
126  * - @ref NT_Done() - Close down the NTAPI library.
127  *
128  *<hr>
129  * @section transmit_on_timestamp_setclock_example_code Code
130  * @}
131  */
132 
133 // Include this in order to access the Napatech API
134 #include <nt.h>
135 
136 #if defined(__linux__) || defined(__FreeBSD__) || (__MINGW32__)
137  #include <unistd.h> // sleep()
138 #endif
139 
140 #if defined(_MSC_VER)
141  #include <timesupport.h>
142 #endif
143 
144 #include <assert.h>
145 
146 #define PACKETS 100L // How many packets to send
147 #define PACKET_SIZE 296L // Packet size to transmit (incl crc.)
148 #define PACKET_GAP 500000L // Distance in 1ns between packets
149 #define REPLAYS 1000L // How many times we will repeat the packets
150 #define TX_DELAY 1000000000 // Initial transmission delay (one second)
151 #define PORT 0
152 #define ADAPTER 0
153 
154 // This program, by default, uses the NT descriptor type to transmit packets.
155 // To use a PCAP descriptor, set the boolean variable pcapPackets to true.
156 bool pcapPackets = false; // Set to true to enable PCAP descriptor type.
157 
158 // Time in nanoseconds to transmit one replay session
159 #define REPLAY_TRANSMIT_TIME (PACKET_GAP * PACKETS)
160 
161 // Time in nanoseconds to transmit all replay sessions
162 #define TOTAL_TRANSMIT_TIME (REPLAY_TRANSMIT_TIME * REPLAYS)
163 
164 // Struct pcap_timespec_s is the time specification for PCAP. For PCAP_NANO,
165 // the ts_usec field is in nanoseconds instead of microseconds.
166 struct pcap_timespec_s {
167  uint32_t ts_sec;
168  uint32_t ts_usec;
169 };
170 
171 // Standard PCAP record header.
173  struct pcap_timespec_s time;
174  uint32_t incl_len;
175  uint32_t orig_len;
176 };
177 
178 // printError is a simple convenience function for printing an NTAPI error
179 // message to stderr.
180 static void printError(const char *prefix, int errorCode) {
181  char errorBuffer[NT_ERRBUF_SIZE];
182  NT_ExplainError(errorCode, errorBuffer, sizeof errorBuffer);
183  fprintf(stderr, "%s: %s\n", prefix, errorBuffer);
184 }
185 
186 static uint64_t
187 convertToUnixNsTs(const struct NtConfigTimestampReadData_s *data)
188 {
189  const struct pcap_timespec_s *pcap_ts = (const struct pcap_timespec_s *)&data->ts;
190  switch (data->tsType) {
192  return pcap_ts->ts_sec * 1000000000ULL + pcap_ts->ts_usec;
194  return data->ts;
195  default:
196  // default to nativeUnixTs when nanosecond resolution unavailable
197  // this is Unix Time with 10ns resolution.
198  return data->nativeUnixTs * 10;
199  }
200 }
201 
202 int main(void)
203 {
204  //
205  // Initialize the API. This also checks if we are compatible with the
206  // installed library version.
207  //
208  int status;
209 
210  if ((status = NT_Init(NTAPI_VERSION)) != NT_SUCCESS) {
211  printError("NT_Init() failed", status);
212  return -1;
213  }
214 
215  //
216  // Open the config stream, in order to retrieve the adapter timestamp and
217  // to configure the transmit on timestamp functionality. We configure
218  // transmit on timestamp in such a way that a packet with timestamp 0 will
219  // transmit in approximately one second from this point.
220  //
221  NtConfigStream_t hConfig;
222  NtConfig_t config;
223 
224  if ((status = NT_ConfigOpen(&hConfig, "replay")) != NT_SUCCESS) {
225  printError("NT_ConfigOpen() failed", status);
226  return -1;
227  }
228 
230  config.u.timestampRead.adapter = ADAPTER;
231 
232  if ((status = NT_ConfigRead(hConfig, &config)) != NT_SUCCESS) {
233  printError("NT_ConfigRead() failed", status);
234  return -1;
235  }
236 
237  // Store the adapter timestamp and convert it to unix nanoseconds.
238  // Nanosecond precision is only available if TimestampFormat = UNIX_NS or
239  // TimestampFormat = PCAP_NS as defined in ntservice.ini.
240  // If TimestampFormat is set to anything else, it will derive the
241  // nanosecond timestamp from the NATIVE timestamp, which is measured
242  // in 10-ns ticks, and hence lose a small amount of precision.
243  uint64_t adapterTS = convertToUnixNsTs(&config.u.timestampRead.data);
244 
245  // Configure transmit on timestamp. In scenarios where a packet descriptor
246  // that does not contain a TXNOW bit is used, such as when transmitting PCAP,
247  // forceTxOnTs must be set to true. However, this example uses NT
248  // descriptors, and can thus use the TXNOW bit.
249  //
250  // timeDeltaAdjust is set to the time it takes to complete a single replay
251  // session, so that the TXSETCLOCK bit will cause timeDelta to be prepared
252  // for the next replay run. Note that since we set TXSETCLOCK on the first
253  // packet of every iteration, including the first one, that the value of
254  // timeDeltaAdjust need to be subtracted from timeDelta to avoid accidentally
255  // delaying the initial transmission.
256  // There must be "room" for one replay session within the delay window.
257  const uint64_t txBeginTS = adapterTS + TX_DELAY;
259  config.u.transmitOnTimestamp.portNo = (uint8_t)PORT;
260  assert(txBeginTS >= REPLAY_TRANSMIT_TIME);
263  config.u.transmitOnTimestamp.data.enable = true;
264  if (pcapPackets) {
265  // PCAP descriptor
266  config.u.transmitOnTimestamp.data.forceTxOnTs = true;
267  } else {
268  // NT descriptor
269  config.u.transmitOnTimestamp.data.forceTxOnTs = false;
270  }
271 
272  if ((status = NT_ConfigWrite(hConfig, &config)) != NT_SUCCESS) {
273  printError("NT_ConfigWrite() failed", status);
274  return -1;
275  }
276 
277  //
278  // Open a TX stream
279  //
280  NtNetStreamTx_t hNetTx;
281  if ((status = NT_NetTxOpen_v2(
282  &hNetTx, "transmit_on_timestamp_example_txstream", 1ULL << PORT,
286  printError("NT_NetTxOpen_v2() failed", status);
287  return -1;
288  }
289 
290  //
291  // Retrieve a packet buffer, fill it, and repeat until we have transmitted
292  // the requested amount of packets. The timestamp will be set so that the
293  // adapter will transmit at approximately 5 Mb/s.
294  //
295  printf("Commencing transmission of %ld packets %ld times\n", PACKETS, REPLAYS);
296 
297  NtNetBuf_t hNetBufTx;
298  int totalPackets = 0;
299 
300  for (int replays = 0; replays < REPLAYS; replays++) {
301 
302  uint64_t timestamp = 0;
303  for (int pkts = 0; pkts < PACKETS; pkts++, totalPackets++) {
304 
305  // Get a packet TX buffer for this tx stream and port, without timeout
306  if ((status = NT_NetTxGet(hNetTx, &hNetBufTx, PORT, PACKET_SIZE,
307  NT_NETTX_PACKET_OPTION_DEFAULT, -1)) != NT_SUCCESS) {
308  printError("NT_NetTxGet() failed", status);
309  return -1;
310  }
311 
312  // Set the packet timestamp
313  if (pcapPackets) {
314  // PCAP descriptor
315  struct pcap_record_s *hdr =
316  (struct pcap_record_s *)NT_NET_GET_PKT_DESCR_PTR(hNetBufTx);
317  hdr->incl_len = PACKET_SIZE;
318  hdr->orig_len = PACKET_SIZE;
319  hdr->time.ts_sec = (uint32_t)(timestamp / 1000000000U);
320  hdr->time.ts_usec = (uint32_t)(timestamp % 1000000000U);
321  } else {
322  // NT descriptor
323  NT_NET_SET_PKT_TIMESTAMP(hNetBufTx, timestamp);
324  }
325 
326  timestamp += PACKET_GAP; // increment the timestamp for the next packet
327 
328  if (pkts == 0) {
329  // We set TXSETCLOCK on the first packet. This results in the adapter
330  // adding timeDeltaAdjust to timeDelta before transmitting this packet.
331  NT_NET_SET_PKT_TXSETCLOCK(hNetBufTx, 1);
332  }
333 
334  // Fill the packet with an incrementing payload. Note that this will result
335  // in a garbage ethernet frame.
336  uint32_t *ptr = (uint32_t*)NT_NET_GET_PKT_L2_PTR(hNetBufTx);
337  for (uint32_t i = 0; i < PACKET_SIZE/4; i++) {
338  *(ptr+i) = i;
339  }
340 
341  // Release the TX buffer to commit the packet. It will transmit once the
342  // timestamp is reached, or when transmit on timestamp is disabled.
343  if ((status = NT_NetTxRelease(hNetTx, hNetBufTx)) != NT_SUCCESS) {
344  printError("NT_NetTxRelease() failed", status);
345  return -1;
346  }
347  }
348  }
349 
350  //
351  // Wait for all packets to be transmitted. There are multiple ways to do
352  // this, but in this example, we read the adapter timestamp in a loop, and
353  // compare it to the value of our timestamp variable from the loop above.
354  //
355  printf("Waiting for transmission to end\n");
356 
358  config.u.timestampRead.adapter = ADAPTER;
359 
360  const uint64_t txStopTS = txBeginTS + TOTAL_TRANSMIT_TIME;
361  uint64_t newAdapterTS;
362  do {
363  sleep(1);
364  if ((status = NT_ConfigRead(hConfig, &config)) != NT_SUCCESS) {
365  printError("NT_ConfigRead() failed", status);
366  return -1;
367  }
368 
369  newAdapterTS = convertToUnixNsTs(&config.u.timestampRead.data);
370  } while (newAdapterTS < txStopTS);
371 
372  //
373  // Disable transmit on timestamp. If any packets are left in the buffera this
374  // point, they will transmit unhindered at line rate.
375  //
377  config.u.transmitOnTimestamp.portNo = (uint8_t)PORT;
378  config.u.transmitOnTimestamp.data.timeDelta = 0;
380  config.u.transmitOnTimestamp.data.enable = false;
381  config.u.transmitOnTimestamp.data.forceTxOnTs = false;
382 
383  if ((status = NT_ConfigWrite(hConfig, &config)) != NT_SUCCESS) {
384  printError("NT_ConfigWrite() failed", status);
385  return -1;
386  }
387 
388  //
389  // Terminate
390  //
391  printf("Done: %d packets sent\n", totalPackets);
392 
393  //Close the Config stream
394  NT_ConfigClose(hConfig);
395 
396  // Close the TX stream
397  NT_NetTxClose(hNetTx);
398 
399  // Close the API
400  NT_Done();
401 
402  return 0;
403 }