inline_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/inline/inline_example.c Source File
inline_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/inline/inline_example.c
45  * @section inline_example_description Description
46  *
47  * INFO: This is an 3GA and 4GA inline example.
48  * A more advanced 4GA inline example can be found in examples/net/vlandemo/.
49  *
50  * This source file is an example of how to run in an inline
51  * scenario. The example will discard every 2nd packet.
52  *
53  * The following NTAPI functions are used:
54  * - @ref NT_Init()
55  * - @ref NT_InfoOpen()
56  * - @ref NT_InfoRead()
57  * - @ref NT_InfoClose()
58  * - @ref NT_ConfigOpen()
59  * - @ref NT_NetRxOpen()
60  * - @ref NT_NTPL()
61  * - @ref NT_NetRxGet()
62  * - @ref NT_NET_SET_PKT_TXIGNORE()
63  * - @ref NT_NET_GET_PKT_WIRE_LENGTH()
64  * - @ref NT_NET_GET_PKT_TIMESTAMP()
65  * - @ref NT_NetRxRelease()
66  * - @ref NT_NetRxClose()
67  * - @ref NT_ConfigClose()
68  * - @ref NT_ExplainError()
69  *
70  * @section inline_example_prerequisites Prerequisites
71  * An Napatech inline accelerator with at least one RX host buffer per port
72  * defined. Below is an example of a minimum ini-file. It will create
73  * two 32MB RX hostbuffers from NUMA node 0. The source and
74  * destination is determined by the filter. Use a traffic generator to
75  * apply traffic to one of the ports.
76  * @code
77  * [System]
78  * TimestampFormat = NATIVE
79  *
80  * [Adapter0]
81  * AdapterType = NT20E
82  * BusId = 00:0a:00.00
83  * HostBuffersRx = [2,32,0]
84  * @endcode
85  *
86  * @section inline_example_flow Program flow
87  * @{
88  * The following is required to run in an inline scenario:
89  * - \#include/nt.h - Applications/Tools only need to include @ref
90  * nt.h to obtain prototypes, macros etc. from NTAPI.
91  * - @ref NT_Init(@ref NTAPI_VERSION) - Initialize the NTAPI
92  * library. @ref NTAPI_VERSION is a define that describes the version
93  * of the API described in the header files included by @ref
94  * nt.h. NT_Init() will ask the NTAPI library to convert return data
95  * to the @ref NTAPI_VERSION if possible. This will ensure that
96  * applications can run on NTAPI libraries of newer versions.
97  * - @ref NT_InfoOpen() - Detect the adapter generation and if TX is
98  * available.
99  * - @ref NT_ConfigOpen() - Open a config stream in order to setup
100  * filter using the @ref NT_NTPL() command.
101  * - @ref NT_NetRxOpen() - Open a stream. The stream ID must match
102  * the one used when creating the filter using the @ref NT_NTPL()
103  * command. A stream doesn't return data until traffic is assigned
104  * to it by creating a filter. Stream IDs might be shared between
105  * other streams and it is possible to make several filters to one
106  * stream ID. Each filter can have a unique color in the ASSIGN. The
107  * "color" of the ASSIGN can be used to mark packets making it
108  * possible for the stream to determine if the packets it receives
109  * via @ref NT_NetRxGet() as based on its assign or if the packet
110  * belongs to the other streams that also share the hostbuffer.
111  * - @ref NT_NTPL() - Assign traffic to a stream by creating a filter
112  * using a manually chosen stream ID. The stream ID must match the
113  * one used @ref NT_NetRxOpen(). On 4GA adapters and above,
114  * the Setup NTPL statement must be used to forward the packets from
115  * the stream to the output tx port (as opposed to the older form on
116  * 3Garch adapters where this could be done in the Assign statement).
117  * To indicate whether a packet should be dropped, we use the DYN3
118  * descriptors and indicate that the wirelength is used.
119  * - Optional step. Wait until we start seeing packets that are hit by
120  * the NTPL assign command. This is done to avoid getting packets
121  * that are not fully classified by the stream. @ref NT_NetRxGet() is
122  * called with a timeout of 1000ms and will return NT_STATUS_TIMEOUT
123  * in case nothing is received within 1000ms and will return
124  * NT_SUCCESS if something is returned. Return values different from
125  * that is an indication of an error. Packets that are prior to the
126  * expected time are released via @ref NT_NetRxRelease().
127  * - @ref NT_NetRxGet(), discard every 2nd packet and @ref NT_NetRxRelease() -
128  * Receive packets, discard every 2nd and release packets. The @ref
129  * PacketMacros are used to find the packet length ant the timestamp
130  * of the packet:
131  * - @ref NT_NET_GET_PKT_WIRE_LENGTH() - Get length of the packet to store.
132  * - @ref NT_NET_GET_PKT_TIMESTAMP() - The time the packet was delivered.
133  * - @ref NT_NET_SET_PKT_TXIGNORE() - Don't send the current packet (3GArch).
134  * - @ref NT_NET_GET_PKT_DESCR_PTR() - Get the packet descriptor header (4GArch).
135  * In the 4GArch, we define bit 41 to hold the TXIGNORE bit position by using the setup
136  * command. Then we set bit 41 or color_lo bit 14 to discard the packet
137  * as opposed to using the NT_NET_SET_PKT_TXIGNORE().
138  * Note: color_lo bit 14 must not be used as by a color i.e. the color value
139  * must not be so large so it will use bit 14.
140  * - @ref NT_NetRxClose() - Close the stream when terminating. This
141  * will close the stream and release the NTPL assignment made on the
142  * hostbuffer.
143  *
144  *<hr>
145  * @section inline_example_code Code
146  * @}
147  */
148 
149 // Include this in order to access the Napatech API
150 #include <nt.h>
151 
152 #if defined(__linux__) || defined(__FreeBSD__)
153 #include <signal.h>
154 #include <unistd.h>
155 #include <stdatomic.h>
156 #endif
157 
158 #include <stdlib.h>
159 
160 #if defined(WIN32) || defined (WIN64)
161  #define snprintf(dst, ...) _snprintf_s((dst), _countof(dst), __VA_ARGS__)
162  static volatile int appRunning = 1; // The application will run as long as appRunning == 1
163 #else
164  static atomic_int appRunning = 1; // The application will run as long as appRunning == 1
165 #endif
166 
167 #define PORT 0
168 
169 /**
170  * The function called when user is pressing CTRL-C
171  */
172 #if defined(WIN32) || defined (WIN64)
173 static BOOL WINAPI StopApplication(int sig)
174 {
175  (void) sig;
176  appRunning = 0;
177  return TRUE;
178 }
179 #else
180 static void StopApplication(int sig)
181 {
182  if (sig == SIGINT)
183  appRunning = 0;
184 }
185 #endif
186 
187 int main(void)
188 {
189  int numPackets=0; // The number of packets received
190  int numBytes=0; // The number of bytes received
191  int numPacketsDiscard=0; // The number of packets discarded
192  int numBytesDiscard=0; // The number of bytes discarded
193  char tmpBuffer[20]; // Buffer to build filter string
194  char errorBuffer[NT_ERRBUF_SIZE]; // Error buffer
195  int status; // Status variable
196  int isFpga4Garch=0; // Flag specifying whether this is 4GA adapter.
197  int isTxSupported=0; // Flag specifying whether Tx is supported on this adapter.
198  NtNetStreamRx_t hNetRx; // Handle to the RX stream
199  NtConfigStream_t hCfgStream; // Handle to a config stream
200  NtNtplInfo_t ntplInfo; // Return data structure from the NT_NTPL() call.
201  NtNetBuf_t hNetBuf; // Net buffer container. Packet data is returned in this when calling NT_NetRxGet().
202  NtInfoStream_t hInfo; // Handle to the Info stream for determining adapter type
203  NtInfo_t info; // Return data structure from NT_InfoRead() calls.
204  const char *ntplFilter; // Temporary variable storing the filter.
205  uint8_t adapterNo; // The adapter number for the port defined as PORT.
206 
207  // Register ctrl+c handler so we are able to stop again
208 #if defined(WIN32) || defined (WIN64)
209  SetConsoleCtrlHandler((PHANDLER_ROUTINE)StopApplication, TRUE);
210 #else
211  struct sigaction newaction; // Ctrl+c signal handler container
212  memset(&newaction, 0, sizeof(newaction));
213  newaction.sa_handler = StopApplication;
214  if (sigaction(SIGINT, &newaction, NULL) < 0) {
215  fprintf(stderr, "Failed to register SIGINT sigaction.\n");
216  exit(EXIT_FAILURE);
217  }
218 #endif
219 
220  // Initialize the NTAPI library and thereby check if NTAPI_VERSION can be used together with this library
221  if ((status = NT_Init(NTAPI_VERSION)) != NT_SUCCESS) {
222  // Get the status code as text
223  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
224  fprintf(stderr, "NT_Init() failed: %s\n", errorBuffer);
225  return -1;
226  }
227 
228  if ((status = NT_InfoOpen(&hInfo, "inline_example")) != NT_SUCCESS) {
229  // Get the status code as text
230  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
231  fprintf(stderr, "NT_InfoOpen() failed: %s\n", errorBuffer);
232  return -1;
233  }
234 
235  // Detect adapter version and feature set
237  info.u.port_v10.portNo = PORT;
238  if ((status = NT_InfoRead(hInfo, &info)) != NT_SUCCESS) {
239  NT_InfoClose(hInfo);
240  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
241  fprintf(stderr, "NT_InfoRead() failed: %s\n", errorBuffer);
242  return -1;
243  }
244 
245  adapterNo = info.u.port_v10.data.adapterNo;
247  snprintf(info.u.property.path, sizeof(info.u.property.path), "Adapter%d.FpgaGeneration", adapterNo);
248  if ((status = NT_InfoRead(hInfo, &info)) != NT_SUCCESS) {
249  NT_InfoClose(hInfo);
250  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
251  fprintf(stderr, "NT_InfoRead() failed: %s\n", errorBuffer);
252  return -1;
253  }
254 
255  isFpga4Garch = info.u.property.data.u.i >= 4;
256 
257  if (isFpga4Garch) {
258  memset(&info, 0, sizeof(NtInfo_t));
261  if ((status = NT_InfoRead(hInfo, &info)) != NT_SUCCESS) {
262  NT_InfoClose(hInfo);
263  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
264  fprintf(stderr, "NT_InfoRead() failed: %s\n", errorBuffer); return -1;
265  }
269  // 4GA adapters with ANL9 feature level support native TX with inline capabilities
270  // NOTE: 4GA adapters with ANL9 feature level doesn't support
271  // NT_NET_SET_PKT_DESCR_TYPE_EXT7 descriptor type
272  isTxSupported=1;
273  }
274  } else {
275  isTxSupported=1;
276  }
277 
278  if ((status = NT_InfoClose(hInfo)) != NT_SUCCESS) {
279  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
280  fprintf(stderr, "NT_InfoClose() failed: %s\n", errorBuffer);
281  return -1;
282  }
283 
284  // Bail out early because inline isn't possible without Tx.
285  if (!isTxSupported) {
286  fprintf(stderr, "error: Tx is not supported on this adapter.\n");
287  return -1;
288  }
289 
290  // Open a config stream to assign a filter to a stream ID.
291  if ((status = NT_ConfigOpen(&hCfgStream, "TestStream")) != NT_SUCCESS) {
292  // Get the status code as text
293  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
294  fprintf(stderr, "NT_ConfigOpen() failed: %s\n", errorBuffer);
295  return -1;
296  }
297 
298  // Assign traffic to stream ID 1 and mask all traffic matching the assign statement color=7.
299  if (isFpga4Garch) {
300  // Setup traffic to stream ID(=1) and Tx port = 0, and use bit 41 == color_low bit 14 to discard packets.
301  if ((status = NT_NTPL(hCfgStream, "Setup[TxDescriptor=Dyn;TxPorts=0;UseWL=True;TxIgnorePos=41] = StreamId == 1",
302  &ntplInfo, NT_NTPL_PARSER_VALIDATE_NORMAL)) != NT_SUCCESS) {
303  // Get the status code as text
304  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
305  fprintf(stderr, "NT_NTPL() failed: %s\n", errorBuffer);
306  fprintf(stderr, ">>> NTPL errorcode: %X\n", ntplInfo.u.errorData.errCode);
307  fprintf(stderr, ">>> %s\n", ntplInfo.u.errorData.errBuffer[0]);
308  fprintf(stderr, ">>> %s\n", ntplInfo.u.errorData.errBuffer[1]);
309  fprintf(stderr, ">>> %s\n", ntplInfo.u.errorData.errBuffer[2]);
310  return -1;
311  }
312  ntplFilter = "Assign[streamid=1;color=7;Descriptor=Dyn3] = All";
313  } else { // 3Garch - for older FPGAs and adapters
314  ntplFilter = "Assign[streamid=1;color=7;txport=1] = All";
315  }
316 
317  if ((status = NT_NTPL(hCfgStream, ntplFilter, &ntplInfo, NT_NTPL_PARSER_VALIDATE_NORMAL)) != NT_SUCCESS) {
318  // Get the status code as text
319  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
320  fprintf(stderr, "NT_NTPL() failed: %s\n", errorBuffer);
321  fprintf(stderr, ">>> NTPL errorcode: %X\n", ntplInfo.u.errorData.errCode);
322  fprintf(stderr, ">>> %s\n", ntplInfo.u.errorData.errBuffer[0]);
323  fprintf(stderr, ">>> %s\n", ntplInfo.u.errorData.errBuffer[1]);
324  fprintf(stderr, ">>> %s\n", ntplInfo.u.errorData.errBuffer[2]);
325  return -1;
326  }
327 
328  // Close the config stream
329  if ((status = NT_ConfigClose(hCfgStream)) != NT_SUCCESS) {
330  // Get the status code as text
331  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
332  fprintf(stderr, "NT_ConfigClose() failed: %s\n", errorBuffer);
333  return -1;
334  }
335 
336  // Get a stream handle with the stream ID. NT_NET_INTERFACE_PACKET specify that we will receive data in a packet based matter.
337  if ((status = NT_NetRxOpen(&hNetRx, "TestStream", NT_NET_INTERFACE_PACKET, 1, -1)) != NT_SUCCESS) {
338  // Get the status code as text
339  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
340  fprintf(stderr, "NT_NetRxOpen() failed: %s\n", errorBuffer);
341  return -1;
342  }
343 
344  // Optional step. Wait for the first packet that hit the NTPL assign command
345  printf("Waiting for the first packet\n");
346  while (1) {
347  if ((status = NT_NetRxGet(hNetRx, &hNetBuf, 1000)) != NT_SUCCESS) {
348  if ((status == NT_STATUS_TIMEOUT) || (status == NT_STATUS_TRYAGAIN)) {
349  // Timeouts are ok, we just need to wait a little longer for a packet
350  continue;
351  }
352  // Get the status code as text
353  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
354  fprintf(stderr, "NT_NetRxGet() failed: %s\n", errorBuffer);
355  return -1;
356  }
357  // We got a packet. Check if the timestamp is newer than when the NTPL assign command was applied
358  if (NT_NET_GET_PKT_TIMESTAMP(hNetBuf) > ntplInfo.ts) {
359  break; // Break out, we have received a packet that is received after the NTPL assign command was applied
360  }
361  // Release the packet, it is too "old".
362  if ((status = NT_NetRxRelease(hNetRx, hNetBuf)) != NT_SUCCESS) {
363  // Get the status code as text
364  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
365  fprintf(stderr, "NT_NetRxRelease() failed: %s\n", errorBuffer);
366  return -1;
367  }
368  }
369 
370  // Forward all packets and skip every second packet until signal received
371  while (appRunning == 1) {
372  static int disc=0;
373  // Check if the packet should be discarded
374  if (disc) {
375  // Discard the current packet
376  if (isFpga4Garch) {
377  // On 4garch, we useTxIgnorePos to discard packet
378  NtDyn3Descr_t *pDyn3 = (NtDyn3Descr_t *)NT_NET_GET_PKT_DESCR_PTR(hNetBuf);
379  // Discard packets by setting bit 41 == color_low bit 14 as defined by the NTPL setup command
380  pDyn3->color_lo = pDyn3->color_lo | 0x2000; // Bit 41 and bit 14 in color_low
381  } else { // 3Garch
382  NT_NET_SET_PKT_TXIGNORE(hNetBuf, 1);
383  }
384  // Increment the number of packets and bytes discarded.
385  numBytesDiscard+=NT_NET_GET_PKT_WIRE_LENGTH(hNetBuf);
386  numPacketsDiscard++;
387  printf("Discarded: %016llx - %d bytes\n", (unsigned long long)NT_NET_GET_PKT_TIMESTAMP(hNetBuf), NT_NET_GET_PKT_WIRE_LENGTH(hNetBuf));
388  } else {
389  // Increment the number of packets and bytes forwarded.
390  numBytes+=NT_NET_GET_PKT_WIRE_LENGTH(hNetBuf);
391  numPackets++;
392  printf("Forwarded: %016llx - %d bytes\n", (unsigned long long)NT_NET_GET_PKT_TIMESTAMP(hNetBuf), NT_NET_GET_PKT_WIRE_LENGTH(hNetBuf));
393  }
394 
395  // Toggle the discard bit
396  disc ^= 1;
397 
398  // Release the current packet
399  if ((status = NT_NetRxRelease(hNetRx, hNetBuf)) != NT_SUCCESS) {
400  // Get the status code as text
401  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
402  fprintf(stderr, "NT_NetRxRelease() failed: %s\n", errorBuffer);
403  return -1;
404  }
405 
406  // Get the next packet
407  while (appRunning == 1) {
408  if ((status = NT_NetRxGet(hNetRx, &hNetBuf, 1000)) != NT_SUCCESS) {
409  if ((status == NT_STATUS_TIMEOUT) || (status == NT_STATUS_TRYAGAIN)) {
410  // Timeouts are ok, we just need to wait a little longer for a packet
411  continue;
412  }
413  // Get the status code as text
414  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
415  fprintf(stderr, "NT_NetRxGet() failed: %s\n", errorBuffer);
416  return -1;
417  }
418  break; // We got a packet
419  }
420  }
421 
422  // Open a config stream to delete a filter.
423  if ((status = NT_ConfigOpen(&hCfgStream, "TestStream")) != NT_SUCCESS) {
424  // Get the status code as text
425  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
426  fprintf(stderr, "NT_ConfigOpen() failed: %s\n", errorBuffer);
427  return -1;
428  }
429 
430  // Delete the filter
431  snprintf(tmpBuffer, sizeof(tmpBuffer), "delete=%d", ntplInfo.ntplId);
432  if ((status = NT_NTPL(hCfgStream, tmpBuffer, &ntplInfo, NT_NTPL_PARSER_VALIDATE_NORMAL)) != NT_SUCCESS) {
433  // Get the status code as text
434  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
435  fprintf(stderr, "NT_NTPL() failed: %s\n", errorBuffer);
436  fprintf(stderr, ">>> NTPL errorcode: %X\n", ntplInfo.u.errorData.errCode);
437  fprintf(stderr, ">>> %s\n", ntplInfo.u.errorData.errBuffer[0]);
438  fprintf(stderr, ">>> %s\n", ntplInfo.u.errorData.errBuffer[1]);
439  fprintf(stderr, ">>> %s\n", ntplInfo.u.errorData.errBuffer[2]);
440  return -1;
441  }
442 
443  // Close the config stream
444  if ((status = NT_ConfigClose(hCfgStream)) != NT_SUCCESS) {
445  // Get the status code as text
446  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
447  fprintf(stderr, "NT_ConfigClose() failed: %s\n", errorBuffer);
448  return -1;
449  }
450 
451  // Close the stream and release the hostbuffer. This will also remove the NTPL assignments performed.
452  NT_NetRxClose(hNetRx);
453 
454  printf("Done: Discarded %d packets out of %d, %d bytes out of %d\n",
455  numPacketsDiscard, numPackets+numPacketsDiscard, numBytes, numBytes+numBytesDiscard);
456  return 0;
457 }