segment_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/segment_inline/segment_inline_example.c Source File
segment_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/segment_inline/segment_inline_example.c
45  * @section segment_inline_example_description Description
46  *
47  * This source file is an example of how to do inline with the segment interface.
48  *
49  * The following NTAPI functions are used:
50  * - @ref NT_Init()
51  * - @ref NT_NetRxOpen()
52  * - @ref NT_NTPL()
53  * - @ref NT_NetRxRead()
54  * - @ref NT_NetRxGet()
55  * - @ref NT_NET_GET_SEGMENT_PTR()
56  * - @ref NT_NET_GET_SEGMENT_LENGTH()
57  * - @ref NT_NET_GET_SEGMENT_TIMESTAMP()
58  * - @ref NT_NetRxRelease()
59  * - @ref NT_NetRxClose()
60  * - @ref NT_Done()
61  * - @ref NT_ExplainError()
62  *
63  * @section segment_inline_example_prerequisites Prerequisites
64  * A Napatech capture accelerator is need to run this example. The ntservice.ini
65  * must have at least one HostBuffersRx defined. Below is an example
66  * of a minimum ini-file. It will create a 32MB RX hostbuffer from
67  * NUMA node 0.
68  * @code
69  * [System]
70  * TimestampFormat = NATIVE
71  *
72  * [Adapter0]
73  * AdapterType = NT20E
74  * BusId = 00:0a:00.00
75  * HostBuffersRx = [1,32,0]
76  * @endcode
77  *
78  * @section capture_example_flow Program flow
79  * @{
80  * The following is required to perform capture of segments to disk:
81  * - \#include/nt.h - Applications/Tools only need to include @ref
82  * nt.h to obtain prototypes, macros etc. from NTAPI.
83  * - @ref NT_Init(@ref NTAPI_VERSION) - Initialize the NTAPI
84  * library. @ref NTAPI_VERSION is a define that describes the version
85  * of the API described in the header files included by @ref
86  * nt.h. NT_Init() will ask the NTAPI library to convert return data
87  * to the @ref NTAPI_VERSION if possible. This will ensure that
88  * applications can run on NTAPI libraries of newer versions.
89  * - @ref NT_NetRxOpen() - Open the stream using a stream ID.
90  * The stream ID must match the one used when creating the
91  * filter.
92  * - @ref NT_NTPL() - Setup traffic forwarding from stream, and use the wirelength
93  * to indicate whether the packet should be dropped or not.
94  * Assign traffic to the stream with Dyn3 descriptor.
95  * A stream does not return data until traffic is assigned to it by a filter.
96  * Stream IDs can be shared between other streams.
97  * - Wait until we start seeing segments that are hit
98  * by the NTPL assign command. This is done to avoid getting
99  * segments that are not fully classified by the stream.
100  * NT_NetRxGet() is called with a timeout of 1000ms and will return
101  * NT_STATUS_TIMEOUT in case nothing is received within 1000ms and
102  * will return NT_SUCCESS when a segment is returned. Segments with NT_NET_GET_SEGMENTLENGTH()==0
103  * can be returned so it is needed to check for the segment length before using data within
104  * the segment. The NT_NET_GET_SEGMENT_TIMESTAMP() macro can still be used on the empty segments.
105  * Return values different from that is an indication of an error. Segments that
106  * are prior to the expected time are released via NT_NetRxRelease().
107  * - NT_NetRxGet(), and NT_NetRxRelease() - Receive
108  * segments, drop every other packet and release segments. The @ref
109  * SegmentMacros are used to find the segment and length and
110  * timestamp of the segment:
111  * - @ref NT_NET_GET_SEGMENT_PTR() - Get a pointer to the segment.
112  * - @ref NT_NET_GET_SEGMENT_LENGTH() - Get length of the segment to store.
113  * - @ref NT_NET_GET_SEGMENT_TIMESTAMP() - The time the segment was delivered.
114  * - @ref _nt_net_build_pkt_netbuf() and @ref _nt_net_get_next_packet() are used to traverse
115  * packets inside a segment. This is usefull if inspection is needed before saving the
116  * segment.
117  * - NT_NetRxClose() - Close the stream when terminating.
118  * This will close the stream and release the NTPL assignment made on the hostbuffer.
119  * - Close captured file
120  * - @ref NT_Done() - Close down the NTAPI library.
121  *
122  *<hr>
123  * @section capture_example_code Code
124  * @}
125  */
126 
127 #include <nt.h>
128 
129 #if defined(__linux__) || defined(__FreeBSD__)
130 #include <signal.h>
131 #include <unistd.h>
132 #include <stdatomic.h>
133 #endif
134 
135 #include <stdlib.h>
136 
137 #if defined(WIN32) || defined (WIN64)
138  #define snprintf _snprintf
139  static volatile int appRunning = 1; // The application will run as long as appRunning == 1
140 #else
141  static atomic_int appRunning = 1; // The application will run as long as appRunning == 1
142 #endif
143 
144 /**
145  * The function called when user is pressing CTRL-C
146  */
147 #if defined(WIN32) || defined (WIN64)
148 static BOOL WINAPI StopApplication(int sig)
149 {
150  (void) sig;
151  appRunning = 0;
152  return TRUE;
153 }
154 #else
155 static void StopApplication(int sig)
156 {
157  if (sig == SIGINT)
158  appRunning = 0;
159 }
160 #endif
161 
162 int main(void)
163 {
164  int numSegments = 0; // The number of segments received
165  int numPackets = 0; // The number of packets received
166  uint64_t numBytes = 0; // The number of bytes received
167  uint64_t numBytesWire = 0; // The number of bytes received on the wire
168  char tmpBuffer[20]; // Buffer to build filter string
169  char errorBuffer[NT_ERRBUF_SIZE]; // Error buffer
170  int status; // Status variable
171  NtNetStreamRx_t hNetRx; // Handle to the RX stream
172  NtConfigStream_t hCfgStream; // Handle to a config stream
173  NtNtplInfo_t ntplInfo; // Return data structure from the NT_NTPL() call.
174  NtNetBuf_t hNetBuf = NULL; // Net buffer container. Segment data is returned in this when calling NT_NetRxGet().
175  struct NtNetBuf_s pktNetBuf; // Packet netbuf structure.
176  int disc = 0; // Counter to toggle discard the packets
177 
178  // Register ctrl+c handler so we are able to stop again
179 #if defined(WIN32) || defined (WIN64)
180  SetConsoleCtrlHandler((PHANDLER_ROUTINE)StopApplication, TRUE);
181 #else
182  struct sigaction newaction; // Ctrl+c signal handler container
183  memset(&newaction, 0, sizeof(newaction));
184  newaction.sa_handler = StopApplication;
185  if (sigaction(SIGINT, &newaction, NULL) < 0) {
186  fprintf(stderr, "Failed to register SIGINT sigaction.\n");
187  exit(EXIT_FAILURE);
188  }
189 #endif
190 
191  // Initialize the NTAPI library and thereby check if NTAPI_VERSION can be used together with this library
192  if ((status = NT_Init(NTAPI_VERSION)) != NT_SUCCESS) {
193  // Get the status code as text
194  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
195  fprintf(stderr, "NT_Init() failed: %s\n", errorBuffer);
196  return -1;
197  }
198 
199  // Open a config stream to assign a filter to a stream ID.
200  if ((status = NT_ConfigOpen(&hCfgStream, "TestStream")) != NT_SUCCESS) {
201  // Get the status code as text
202  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
203  fprintf(stderr, "NT_ConfigOpen() failed: %s\n", errorBuffer);
204  return -1;
205  }
206 
207  // Setup traffic to stream ID(=1) and Tx port = 0
208  if ((status = NT_NTPL(hCfgStream, "Setup[TxDescriptor=Dyn;TxPorts=0;UseWL=True] = StreamId == 1", &ntplInfo, NT_NTPL_PARSER_VALIDATE_NORMAL)) != NT_SUCCESS) {
209  // Get the status code as text
210  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
211  fprintf(stderr, "NT_NTPL() failed: %s\n", errorBuffer);
212  fprintf(stderr, ">>> NTPL errorcode: %X\n", ntplInfo.u.errorData.errCode);
213  fprintf(stderr, ">>> %s\n", ntplInfo.u.errorData.errBuffer[0]);
214  fprintf(stderr, ">>> %s\n", ntplInfo.u.errorData.errBuffer[1]);
215  fprintf(stderr, ">>> %s\n", ntplInfo.u.errorData.errBuffer[2]);
216  return -1;
217  }
218 
219  // Assign traffic to stream ID 1 and mask all traffic matching the assign statement.
220  if ((status = NT_NTPL(hCfgStream, "Assign[StreamId=1;Descriptor=Dyn3] = Port == 0", &ntplInfo, NT_NTPL_PARSER_VALIDATE_NORMAL)) != NT_SUCCESS) {
221  // Get the status code as text
222  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
223  fprintf(stderr, "NT_NTPL() failed: %s\n", errorBuffer);
224  fprintf(stderr, ">>> NTPL errorcode: %X\n", ntplInfo.u.errorData.errCode);
225  fprintf(stderr, ">>> %s\n", ntplInfo.u.errorData.errBuffer[0]);
226  fprintf(stderr, ">>> %s\n", ntplInfo.u.errorData.errBuffer[1]);
227  fprintf(stderr, ">>> %s\n", ntplInfo.u.errorData.errBuffer[2]);
228  return -1;
229  }
230 
231  // Close the config stream
232  if ((status = NT_ConfigClose(hCfgStream)) != NT_SUCCESS) {
233  // Get the status code as text
234  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
235  fprintf(stderr, "NT_ConfigClose() failed: %s\n", errorBuffer);
236  return -1;
237  }
238 
239  // Open stat stream
240  NtStatStream_t hStat = NULL;
241  if ((status = NT_StatOpen(&hStat, "hStat")) != 0) {
242  fprintf(stderr, "Failed to create statistics stream: 0x%08X\n", status);
243  return -1;
244  }
245 
246  // Reset stats
247  static NtStatistics_t statSet;
249  statSet.u.query_v4.poll = 1;
250  statSet.u.query_v4.clear = 1;
251  if ((status = NT_StatRead(hStat, &statSet))) {
252  fprintf(stderr, "Failed resetting statistics: 0x%08X\n", status);
253  return -1;
254  }
255 
256  // Get a stream handle with stream ID 1. NT_NET_INTERFACE_SEGMENT specify that we will receive data in a segment based matter.
257  if ((status = NT_NetRxOpen(&hNetRx, "TestStream", NT_NET_INTERFACE_SEGMENT, 1, -1)) != NT_SUCCESS) {
258  // Get the status code as text
259  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
260  fprintf(stderr, "NT_NetRxOpen() failed: %s\n", errorBuffer);
261  return -1;
262  }
263 
264  // Optional step. Wait for the first packet that hit the NTPL assign command
265  printf("Waiting for the first segment\n");
266 
267  while (appRunning == 1) {
268  if ((status = NT_NetRxGet(hNetRx, &hNetBuf, 1000)) != NT_SUCCESS) {
269  if ((status == NT_STATUS_TIMEOUT) || (status == NT_STATUS_TRYAGAIN)) {
270  // Timeouts are ok, we just need to wait a little longer for a segment
271  continue;
272  }
273  // Get the status code as text
274  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
275  fprintf(stderr, "NT_NetRxGet() failed: %s\n", errorBuffer);
276  return -1;
277  }
278 
279  // We got a segment. Check if the timestamp is newer than when the NTPL assign command was applied
280  if (NT_NET_GET_SEGMENT_TIMESTAMP(hNetBuf) > ntplInfo.ts) {
281  break; // Break out, we have received a segment that is received after the NTPL assign command was applied
282  }
283 
284  // Release the segment as it is too "old".
285  if ((status = NT_NetRxRelease(hNetRx, hNetBuf)) != NT_SUCCESS) {
286  // Get the status code as text
287  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
288  fprintf(stderr, "NT_NetRxRelease() failed: %s\n", errorBuffer);
289  return -1;
290  }
291  }
292 
293  // Process segments
294  while (appRunning == 1) {
295  if (hNetBuf != NULL) {
296  if (NT_NET_GET_SEGMENT_LENGTH(hNetBuf)) {
297  // Start by building a packet netbuf structure
298  _nt_net_build_pkt_netbuf(hNetBuf, &pktNetBuf);
299  do {
300  // Just count the amount of packets and wire length
301  numPackets++;
302  numBytesWire += NT_NET_GET_PKT_WIRE_LENGTH((&pktNetBuf));
303 
304  // Discard every other packet
305  if (disc) {
306  NtDyn3Descr_t *pDyn3 = (NtDyn3Descr_t *)NT_NET_GET_PKT_DESCR_PTR(&pktNetBuf);
307  pDyn3->wireLength = 0;
308  }
309 
310  // Toggle the discard bit
311  disc ^= 1;
312  } while (_nt_net_get_next_packet(hNetBuf, NT_NET_GET_SEGMENT_LENGTH(hNetBuf), &pktNetBuf)>0);
313 
314  // Increment the number of segments processed.
315  numSegments++;
316 
317  // Increment the bytes received
318  numBytes += NT_NET_GET_SEGMENT_LENGTH(hNetBuf);
319  printf("%016llx - Received segment of %lu bytes.\n",
320  (unsigned long long)NT_NET_GET_SEGMENT_TIMESTAMP(hNetBuf), NT_NET_GET_SEGMENT_LENGTH(hNetBuf));
321  }
322 
323  // Release the current segment
324  if ((status = NT_NetRxRelease(hNetRx, hNetBuf)) != NT_SUCCESS) {
325  // Get the status code as text
326  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
327  fprintf(stderr, "NT_NetRxRelease() failed: %s\n", errorBuffer);
328  return -1;
329  }
330  }
331 
332  // Get the next segment
333  while (1) {
334  if ((status = NT_NetRxGet(hNetRx, &hNetBuf, 1000)) != NT_SUCCESS) {
335  if ((status == NT_STATUS_TIMEOUT) || (status == NT_STATUS_TRYAGAIN)) {
336  // Timeouts are ok, we just need to wait a little longer for a segment
337  continue;
338  }
339  // Get the status code as text
340  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
341  fprintf(stderr, "NT_NetRxGet() failed: %s\n", errorBuffer);
342  return -1;
343  }
344  break; // We got a segment
345  }
346  }
347 
348  // Close the stream and release the hostbuffer. This will also remove the NTPL assignments performed.
349  NT_NetRxClose(hNetRx);
350 
351  // Request stats
353  statSet.u.query_v4.poll = 1;
354  statSet.u.query_v4.clear = 0;
355  if ((status = NT_StatRead(hStat, &statSet)) != NT_SUCCESS) {
356  fprintf(stderr, "Failed reading statistics: 0x%08X\n", status);
357  return -1;
358  }
359 
360  // Read drop counters for streamid 1
361  uint64_t totDropsPkts = statSet.u.query_v4.data.stream.streamid[1].drop.pkts;
362  uint64_t totDropsBytes = statSet.u.query_v4.data.stream.streamid[1].drop.octets;
363 
364  // Close stat stream
365  NT_StatClose(hStat);
366 
367  // Open a config stream to delete a filter.
368  if ((status = NT_ConfigOpen(&hCfgStream, "TestStream")) != NT_SUCCESS) {
369  // Get the status code as text
370  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
371  fprintf(stderr, "NT_ConfigOpen() failed: %s\n", errorBuffer);
372  return -1;
373  }
374 
375  // Delete the filter
376  snprintf(tmpBuffer, sizeof(tmpBuffer), "delete=%d", ntplInfo.ntplId);
377  if ((status = NT_NTPL(hCfgStream, tmpBuffer, &ntplInfo, NT_NTPL_PARSER_VALIDATE_NORMAL)) != NT_SUCCESS) {
378  // Get the status code as text
379  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
380  fprintf(stderr, "NT_NTPL() failed: %s\n", errorBuffer);
381  fprintf(stderr, ">>> NTPL errorcode: %X\n", ntplInfo.u.errorData.errCode);
382  fprintf(stderr, ">>> %s\n", ntplInfo.u.errorData.errBuffer[0]);
383  fprintf(stderr, ">>> %s\n", ntplInfo.u.errorData.errBuffer[1]);
384  fprintf(stderr, ">>> %s\n", ntplInfo.u.errorData.errBuffer[2]);
385  return -1;
386  }
387 
388  // Close the config stream
389  if ((status = NT_ConfigClose(hCfgStream)) != NT_SUCCESS) {
390  // Get the status code as text
391  NT_ExplainError(status, errorBuffer, sizeof(errorBuffer));
392  fprintf(stderr, "NT_ConfigClose() failed: %s\n", errorBuffer);
393  return -1;
394  }
395 
396  printf("Drop: %16lu packets, %16lu bytes\n", totDropsPkts, totDropsBytes);
397  printf("Done: %16d segments, %16d packets, %16lu bytes, %16lu bytes on wire\n", numSegments, numPackets, numBytes, numBytesWire);
398 
399  // Close down the NTAPI library
400  NT_Done();
401 
402  return 0;
403 }
404 
405 //
406 // EOF
407 //